为 SQLite 增加数字清理与解析能力
当数据以 -$1,234.56、12.5% 这样的字符串保存时,直接求和或按数值排序并不方便。一种解决方式是为 SQLite 增加自定义函数 parse_number(value),先清理格式字符,再解析数值。
这次原型实验采用以下规则:
- 移除逗号、美元符号、英镑符号和百分号。
- 处理开头的负号。
- 将能够解析的内容转换为数值,无法解析时返回
NULL。
移除百分号不等于除以 100。 按照这里的规则,12.5% 清理后对应的是 12.5,而不是 0.125。实现此类函数时,需要先明确业务所需的是字符清理还是百分比换算。
实验借助 Claude 生成并迭代实现,对比了 C 与 Rust 两条路线,关注运行性能、代码复杂度和编译后的动态库体积。
第一轮:C 体积更小,Rust 解析更快
初版扩展的动态库体积如下:
| 实现方式 | 动态库体积 |
|---|---|
| C | 14 KB |
| Rust,使用 rusqlite | 321 KB |
| Rust,使用 sqlite-loadable-rs | 298 KB |
C 扩展的体积明显更小,但初版运行性能落后于两个 Rust 实现。实验将这一差异归因于数字解析环节:Rust 对应的浮点解析实现,比初版 C 使用的 strtod 更快。
这里值得区分两个指标:动态库体积小,并不意味着数字解析速度一定更快。
第二轮:替换 C 的数字解析实现
接下来的优化没有更换语言,而是为 C 编写十进制数字解析函数,并采用 Clinger 快速路径(Clinger’s fast path)。
在 200 万行数据的测试中,各实现的每行净耗时如下,数值越低越好:
| 数据类型 | 初版 C | 优化后 C | Rust:rusqlite | Rust:sqlite-loadable-rs |
|---|---|---|---|---|
| 混合数据 | 67 ns | 19 ns | 46 ns | 41 ns |
| 仅整数 | 25 ns | 16 ns | 36 ns | 32 ns |
| 仅小数 | 106 ns | 18 ns | 55 ns | 47 ns |
优化后的 C 实现在三组测试中都取得了最低的每行净耗时。其中,小数数据的变化最明显:从 106 ns 降至 18 ns。
这次变化说明,在该原型中,数字解析实现的选择足以改变性能对比结果,不能只根据实现语言判断运行速度。
如何理解这组结果
这类实验适合拆开看:
- 体积方面:初版 C 动态库为 14 KB,小于两个 Rust 版本。
-
初版性能方面:两个 Rust 实现均快于使用
strtod的初版 C。 - 优化后性能方面:采用 Clinger 快速路径的 C 实现在三组测试中均领先。
对于写好标智库读者关心的技术选型问题,这个案例更适合作为“识别并优化具体解析环节”的参考,而不是 C 与 Rust 的通用性能排名。
这些结果只适用于本次原型比较。 当前材料没有给出完整的测试环境、输入分布、编译选项及误差范围,也没有展开代码复杂度的具体对比。因此,不能据此认定某种语言普遍更快,也不能将初版的体积数据直接视为所有构建配置下的结果。
实际采用类似扩展前,应先明确清理与解析规则,再结合目标数据和部署条件验证性能、体积与行为是否符合需求。

Top comments (0)