借助 Claude 实测 SQLite 扩展的 C 与 Rust
作者使用 Claude 探索数字字符串解析函数的 SQLite 扩展实现,对比 C 与 Rust 的性能、复杂度和产物体积。初始 C 扩展仅 14 KB,但 Rust 解析更快;随后作者继续用提示推动 C 解析实现优化,并按每行净开销进行测量。
AI 深度解读
给 SQLite 加一个 parse_number(value),把 -$1,234.56、12.5% 这样的文本转成能求和、排序的数字,C 和 Rust 该选谁?Simon Willison 用 Claude(Opus 5.5)做了一轮探索:C 扩展体积小得多,Rust 版本却跑得更快。
这次实验最有用的地方,是作者没有停在语言胜负上,而是继续追问解析函数的差异。不过,提供的原文缺少最终性能数字,函数语义也有空白,需要把观察结果和已经验证的结论分开看。
先确定要解析什么,再比较实现
作者设想的接口是 parse_number(value):去掉字符串里的 ,、$、£ 和 %,处理开头的负号,解析不了就返回 SQL 的 NULL。
这类函数适合处理以文本形式保存的金额、比例数据。转换后,SQLite 可以按数值求和、排序,应用层也能少写一遍清洗逻辑。
但需求里有个容易忽略的问题:12.5% 应该得到 12.5,还是 0.125?按「去掉 %」的字面要求,前者更直接;原文没有明确说明。空字符串、空格、多个负号、异常逗号位置如何处理,也没有交代。
解析规则不明确,性能比较就可能是在比较两种不同的行为。
这不是要求一个小实验先写完整规格,而是提醒读者:把原型放进业务之前,先补齐这些边界。
C 只有 14 KB,为什么 Rust 反而更快
作者最初让 Claude 同时比较性能、代码复杂度和扩展文件体积,得到的体积结果是:
| 实现方式 | 扩展文件体积 |
|---|---|
| C | 14 KB |
| Rust,使用 Rusqlite | 321 KB |
| Rust,使用 sqlite-loadable-rs | 298 KB |
体积上,C 的优势很明显。但作者报告,Rust 扩展的执行速度更快。
他将原因归结为数字解析:Rust 中对应 C strtod 的解析实现「快两倍」。这里应当区分两个层次:底层解析函数快两倍,不等于整个 SQLite 扩展的吞吐量也翻倍。一次 SQL 函数调用还包含取值、字符处理、结果返回等开销。
接着,作者要求 Claude 自己写一个 C 十进制解析函数,争取追平或超过 Rust 实现。这一步很关键:它把问题从「哪门语言快」,缩小到了「哪段解析代码更适合这项任务」。
原文提到,测试使用 200 万行数据的每行净成本,数值越低越好。但提供的内容没有实际测试数值,也没有对应图表。因此,我们无法确认手写 C 最后有没有追平 Rust,更无法给出它们的性能差距。
我的判断
我认可作者继续追查解析函数的做法。拿 C 的通用 strtod 与另一套解析实现比较,测到的差异既包含语言选择,也包含库和算法选择,不能直接归纳成「Rust 比 C 快」。
手写解析器也有前提。如果只需要支持有限格式,它可能省掉通用函数的处理成本;但可接受输入越多,边界条件和维护负担就越重。原文没有提供完整实现、测试用例或最终性能数据,暂时不能判断这笔交换是否划算。
代码复杂度同样没有足够证据。作者确实要求比较这一项,但提供的正文没有代码、代码行数或维护成本对照,不能只凭文件体积判断 C 更简单。
AI 在这里展示了一种可借鉴的探索流程:先做两个版本,观察差异,再围绕差异提出下一轮问题。最后,作者还让 Claude 将调查整理成符合 simonw/research 仓库风格的报告,并提供包含新目录的 zip 文件。但这只能说明作者如何组织产物,不能替代对产物的审查。
能拿来做什么
- 如果前端或 Node 项目使用 SQLite 保存金额、比例文本,可以先列出输入与预期输出,尤其明确
%的含义,以及哪些异常输入必须返回NULL。 - 比较 C、Rust 扩展时,把文件体积和执行速度分别记录。原文里的 14 KB、321 KB、298 KB 是这次实验的结果,不宜直接当作自己项目的预估。
- 用 AI 编程做性能探索时,可以沿用「实现两个版本,再追查热点函数」的提问方式;让它同时交付测试输入、运行环境和计时方法,才方便复核。
- 准备替换
strtod时,先核对手写解析器与原有实现的行为是否一致,再讨论速度收益。