跳到正文
Simon Willison·· 4 小时前AI 评分22

用 C 和 Rust 分别实现 SQLite 扩展 parse_number():性能对比

parse_number() as a SQLite extension: C vs Rust

AI 导读

四个 SQLite 扩展用 C 和 Rust 实现相同的 parse_number() 行为,测试覆盖类型保留、解析规则与浮点精度。优化后的 C 实现每行 16–19 ns,约为 Rust 版本的两倍速度,也远快于 SQL 的 replace()。普通 C 版本因依赖 strtod() 在小数上偏慢,加入紧凑快速路径后补齐差距;所有版本在 800 万次模糊测试中结果一致。

正文 · AI 翻译

研究 parse_number() 作为 SQLite 扩展:C 与 Rust 对比 —— 四个 SQLite 扩展分别用 C 和 Rust 实现了相同的 `parse_number()` 行为,测试覆盖类型保留、解析规则和浮点精度。优化后的 C 实现最快,每行 16–19 ns,约为 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