parse_number() SQLite 扩展实测:C 与 Rust 性能对比
parse_number() as a SQLite extension: C vs Rust
Simon Willison 借助 Claude(Opus 5.5)用 C 和 Rust 分别实现 SQLite 扩展函数 parse_number(),将 -$1,234.56、'12.5%' 等字符串转为可排序求和的数值。
研究 用 C 与 Rust 实现 SQLite 扩展 parse_number() 的对比 —— 四个 SQLite 扩展分别用 C 和 Rust 实现相同的 `parse_number()` 行为,测试涵盖类型保留、解析规则和浮点精度。优化后的 C 实现最快,每行仅需 16–19 纳秒,约是 Rust 版本的两倍,远快于 SQL `replace()`。纯 C 版本在处理小数时较慢,因为它依赖 `strtod()`,而一条紧凑的快速路径弥补了这一差距;所有版本在 800 万次模糊测试中结果完全一致。
我正遛狗的时候,忽然好奇:写一个 SQLite C 扩展来添加一个 parse_number() 函数,把像 -$1,234.56 或 '12.5%' 这样的字符串转换成可以求和与排序的数值,到底有多容易/困难。
我打开了普通的 Claude 聊天(Opus 5.5),输入了这样的提示:
I'm thinking about a SQLite custom function 'parse_number(value)' that strips ',', '$', '£' and '%', handles a leading '-', and returns 'NULL' for anything it can’t parse
Explore the difference between building this as a SQLite extension in C v.s. in Rust - I want to compare performance and code complexity and size of the resulting.so(or whatever) file
这给了我一个不错的起点——Rust 扩展为 321 KB(使用 Rusqlite)和 298 KB(使用 sqlite-loadable-rs),而 C 扩展只有 14 KB。但 Rust 扩展反而更快!
原来这是因为 C 的 strtod 的 Rust 等价实现要“快两倍”。于是我提示道:
Try writing your own C function for parsing text into a decimal number that catches up to or beats the Rust implementation
它照做了:
处理 200 万行的每行净开销,越低越好:
初版 C 新版 C rusqlite sqlite-loadable 混合数据 67 ns 19 ns 46 ns 41 ns 仅整数 25 ns 16 ns 36 ns 32 ns 仅小数 106 ns 18 ns 55 ns 47 ns
最后,我让它把原型重写为一篇研究报告,以便收录进 simonw/research,提示如下:
Clone simonw/research from GitHub and turn your above investigation into a research report as seen in that repository - you can give me a zip file to download that contains your new folder
这是最终生成的报告。
来源: Simon Willison · simonwillison.net