孤本网
/ 0 阅读
0
0

SQLite 扩展开发对比:手写 C 解析函数性能超越 Rust 方案

一句话结论

手写优化 C 版 SQLite 扩展在数字解析性能上击败 Rust 实现,且文件体积仅为后者的百分之五。

关键要点

  • 基于 Rusqlite 的 Rust 扩展体积为 321 KB,基于 sqlite-loadable-rs 的 Rust 扩展为 298 KB,而 C 扩展仅 14 KB。
  • 在混合数据测试中,标准 C 版每行耗时 67 纳秒,手写优化 C 版降至 19 纳秒,Rusqlite 版 46 纳秒,sqlite-loadable-rs 版 41 纳秒。
  • 在纯整数数据测试中,手写优化 C 版每行耗时 16 纳秒,优于标准 C 版的 25 纳秒和两种 Rust 方案(分别为 36 纳秒和 32 纳秒)。
  • 在纯小数数据测试中,手写优化 C 版每行耗时 18 纳秒,显著低于标准 C 版的 106 纳秒和 Rust 方案(分别为 55 纳秒和 47 纳秒)。
  • 该 SQLite 自定义函数 parse_number(value) 支持去除逗号、美元符号、英镑符号和百分号,处理前置负号,并在无法解析时返回 NULL。

背景与事实

2026 年 11 月 8 日,开发者 Simon Willison 在遛狗期间产生了对 SQLite 扩展性能的好奇,随即使用 Claude(Opus 5.5 版本)进行技术探讨。他提出的核心需求是构建一个名为 parse_number() 的 SQLite 自定义函数,该函数需能够处理类似“-$1,234.56”或“'12.5%'”的字符串,将其转换为可求和和排序的数值类型,并明确指定了处理规则:剥离逗号、“$”、“£”和“%”字符,处理前置的“-”符号,对于无法解析的内容返回“NULL”值。

初始探索阶段重点对比了使用 C 语言和 Rust 语言构建该 SQLite 扩展的性能与复杂度差异。数据显示,使用 Rusqlite 库构建的 Rust 扩展最终生成文件大小为 321 KB,而使用 sqlite-loadable-rs 构建的 Rust 扩展为 298 KB。相比之下,使用 C 语言构建的扩展文件仅为 14 KB。令人意外的是,尽管 C 版文件极小,但 Rust 版本在初始测试中表现更快。Simon Willison 分析认为,这是因为 Rust 中对应 C 语言“strtod”函数的实现效率约为后者的两倍。

基于这一发现,后续步骤要求将 C 语言解析逻辑重新设计,以匹配或超越 Rust 版本的性能。最终生成的手写优化 C 函数在 200 万行数据的测试中展现了显著优势。具体数据如下:在混合数据场景下,初版 C 函数每行处理成本为 67 纳秒,重写后的 C 函数降至 19 纳秒,而 Rusqlite 版本为 46 纳秒,sqlite-loadable-rs 版本为 41 纳秒。在仅包含整数的场景中,初版 C 函数为 25 纳秒,重写后为 16 纳秒,Rusqlite 为 36 纳秒,sqlite-loadable-rs 为 32 纳秒。在仅包含小数的场景中,初版 C 函数耗时高达 106 纳秒,重写后大幅降至 18 纳秒,而两种 Rust 方案分别为 55 纳秒和 47 纳秒。Simon Willison 随后将该调研过程整理为标准研究报告,并归档至其 GitHub 仓库 simonw/research 中。

影响分析

对于中文开发者与从业者而言,这一对比提供了关于 SQLite 扩展开发语言选择的实证参考。传统观念认为 Rust 因现代语言特性通常优于 C,但在特定的基础数学解析任务中,手写优化的 C 代码在延迟敏感型场景下更具优势。由于 C 扩展体积仅 14 KB,其在边缘计算、嵌入式系统或对加载时间敏感的应用中具备极高的部署效率。对于数据工程团队,若需在高吞吐量的离线数据清洗中处理大量非标准化数字字符串,采用 C 语言实现底层扩展可能比使用 Rust 绑定带来更低的 CPU 开销。然而,这也意味着团队需要承担 C 语言内存安全管理的风险,开发者需权衡开发效率与极致性能之间的关系。

适用边界

该性能结论仅适用于对字符串到浮点数转换的高频操作场景,且数据量达到数百万行级别。若应用场景涉及复杂的文本预处理、多语言字符编码处理或需要频繁修改解析规则,Rust 提供的类型安全和标准库便利性可能优于 C 语言。此外,文中性能数据基于特定硬件环境与测试数据分布,若服务器架构、数据库并发量或数据特征(如字符串长度极短或极长)发生显著变化,C 与 Rust 的相对性能优势可能会发生逆转。该结论不直接适用于需要与 Python 等其他宿主语言进行频繁交互的扩展开发场景,因为跨语言调用开销可能掩盖底层解析的性能差异。

孤本观察

基于本文事实,值得注意的是,初始 Rust 版本的优势主要源于标准库函数的优化,而非语言本身固有特性;一旦剥离库函数依赖改为手写算法,C 语言在底层指令层面的可微调性反而成为性能决定因素,这表明在极致性能优化中,人工干预底层逻辑的价值高于高级语言抽象。

常见问题

手写优化 C 扩展与 Rust 扩展在混合数据测试中的每行耗时分别是多少?

手写优化 C 版每行耗时 19 纳秒,Rusqlite 版为 46 纳秒,sqlite-loadable-rs 版为 41 纳秒。

C 扩展与基于 Rusqlite 的 Rust 扩展的文件体积分别是多少?

C 扩展文件体积为 14 KB,基于 Rusqlite 的 Rust 扩展文件体积为 321 KB。

parse_number() 函数支持处理哪些特定符号或字符?

支持去除逗号、美元符号、英镑符号和百分号,并处理前置负号;无法解析时返回 NULL。

在纯小数数据测试中,重写后的 C 函数相比初版 C 函数性能提升了多少?

初版 C 函数每行耗时 106 纳秒,重写后降至 18 纳秒,性能大幅降低耗时。

来源:Simon Willison


评论