返回 文章 学习 CMS 文章

SQLite 扩展 parse_number() 的 C 与 Rust 实现对比研究

一次由遛狗好奇心驱动的 AI 辅助实验:用 C 和 Rust 分别实现同一个 SQLite 字符串转数字扩展,比较性能、代码复杂度与产物体积。

SQLiteCRust性能优化
成长分 / 100 57 综合收获、行动、留存与影响

为什么值得读展示了一个完整的 AI 辅助工程实验流程:从模糊想法到提示词迭代、性能基准测试,再到整理成研究报告。

提供了 C 与 Rust 编写 SQLite 扩展在性能、体积和代码复杂度上的具体量化对比数据。

关键洞察
  1. 四种实现(两种 Rust、两种 C)行为一致,在 800 万次模糊测试中结果完全匹配。
  2. 初版 C 因依赖 strtod() 在纯小数场景较慢(106 ns/行),加入紧凑快速路径后降至 18 ns/行。
  3. 优化后的 C 版本在混合数据、纯整数、纯小数三类场景下均为最快,约 16–19 ns/行。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:2710

研究 将 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 扩展更快!

原来这是因为 Rust 中对应 C 的 strtod 的实现“快两倍”。于是我提示:

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

这是最终生成的报告。