parse_number() 作为 SQLite 扩展:C 与 Rust
我正遛狗时,突然好奇:编写一个 SQLite C 扩展来添加 parse_number() 函数,把 -$1,234.56 或 '12.5%' 这样的字符串转换成可求和、可排序的数值,会有多容易或多困难。
我打开普通的 Claude 聊天(Opus 5.5),输入了提示:
我正在考虑一个 SQLite 自定义函数 'parse_number(value)',它会去掉 ','、'$'、'£' 和 '%',处理前导 '-',并对任何无法解析的内容返回 'NULL' 探索用 C 与用 Rust 将其构建为 SQLite 扩展的差异——我想比较性能、代码复杂度以及生成的 .so(或类似)文件的大小
我正在考虑一个 SQLite 自定义函数 'parse_number(value)',它会去掉 ','、'$'、'£' 和 '%',处理前导 '-',并对任何无法解析的内容返回 'NULL'
探索用 C 与用 Rust 将其构建为 SQLite 扩展的差异——我想比较性能、代码复杂度以及生成的 .so(或类似)文件的大小
这给了我一个可靠的起点——Rust 扩展是 321 KB(使用 Rusqlite)和 298 KB(使用 sqlite-loadable-rs),而 C 扩展是 14 KB。但 Rust 扩展更快!
原来这是因为 C 的 strtod 在 Rust 中的对应实现“快两倍”。于是我又输入了提示:
试着编写你自己的 C 函数,将文本解析为十进制数,使其追平或超过 Rust 实现
它做到了,使用的是“Clinger's fast path”:
在 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
在 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 中的其他报告,并输入了这段提示:
从 GitHub 克隆 simonw/research,并把你上面的调查改写成该仓库中所见的那种研究报告——你可以给我一个可供下载的 zip 文件,其中包含你的新文件夹
这是最终得到的报告。