文章摘要
借助AI辅助,如今可以轻松实现微秒级(约5μs)的JIT编译,大幅降低编译耗时。以pgrust数据库为例,其JIT编译器可为每条SQL查询即时生成汇编代码,性能提升显著。文章通过构建正则表达式引擎示例,展示了快速JIT编译的实现方法。
文章总结
好的,这是根据您的要求,对原文进行的中文重述,保留了核心细节,并删减了与主题无关的内容(如文末的推广和社交媒体链接)。
标题:在5微秒内完成JIT编译
核心观点: 借助AI,编写一个编译速度极快的JIT编译器已不再是难事。这为新一代数据库(如pgrust)提供了超越传统数据库的机会。pgrust的JIT编译器能在约5微秒内完成代码编译,从而可以对每一个SQL查询进行JIT编译,而非仅限部分查询。
JIT编译的价值: JIT编译即在运行时生成编译代码。当正确应用时,它能带来2-5倍甚至更高的性能提升。其核心应用场景是,当程序在运行时才能获得关键信息(如编程语言解释器接收待执行代码,或解析未知模式的数据)时,JIT编译能显著优化性能。
示例:构建一个简单的正则表达式引擎
为了演示,文章构建了一个仅支持字面量字符串和重复(*)功能的简易正则引擎,并用Rust结构体表示正则表达式(Literal、Concatenation、Repetition)。
- 解释器实现: 一个约20行代码的解释器,通过递归匹配节点。性能测试显示,其速度比针对特定正则(如
b(an)*)手写的代码慢10-20倍。
JIT编译的实现方法:
生成汇编代码: 采用“复制-粘贴”方法。预先为不同操作(如字符匹配、循环、失败处理)编写好汇编模板(称为“stencils”)。在JIT编译时,根据具体操作(如要匹配的字符、跳转地址)对模板进行微调,然后将这些填充后的模板拼接起来,生成完整的运行时程序。
具体步骤(以ARM64架构为例):
- 设计约定: 使用栈进行回溯;待匹配字符串以空字符结尾,省去长度检查。
- 寄存器分配:
x0(当前字符串位置/返回值),x1(栈顶),x2(栈底),x9(临时变量)。 - 生成的汇编代码结构:
- 序言: 初始化栈。
- 字符匹配: 加载字符,与目标字符比较,不匹配则跳转到失败处理。
- 重复(循环): 将循环结束后的地址和当前字符串位置压入栈,然后执行循环体(匹配
a和n),匹配成功则跳回循环开始。 - 匹配成功: 检查是否到达字符串末尾(空字符),是则返回1。
- 失败处理: 检查栈是否为空,不为空则弹出之前保存的地址和位置并跳转(回溯),为空则返回0。
构建Stencils: 为每个操作(序言、字符匹配、循环开始、跳转、匹配成功、失败处理)编写Rust函数,这些函数接收参数(如字符、地址偏移)并生成对应的机器码(
u32数组)。代码发射器: 一个
Emitter结构体,负责递归遍历正则表达式的AST,调用相应的stencil函数生成机器码,并填充正确的地址偏移(如失败处理块的地址、循环跳转的目标地址)。加载机器码: 使用
mmap分配一块可读、可写、可执行的内存。将生成的机器码复制进去,然后通过transmute将其转换为一个可调用的函数指针。
性能结果:
| 输入长度 | 解释器 | JIT | 手写代码 | JIT加速比 | 手写代码加速比 | | :--- | :--- | :--- | :--- | :--- | :--- | | 9 | 45 ns | 3.8 ns | 3.8 ns | 11.7x | 11.9x | | 33 | 103 ns | 7.9 ns | 10.5 ns | 13.0x | 9.8x | | 129 | 597 ns | 30 ns | 32 ns | 19.7x | 18.6x | | 513 | 1,955 ns | 126 ns | 120 ns | 15.5x | 16.2x | | 2,049 | 8,301 ns | 470 ns | 393 ns | 17.7x | 21.1x |
结论: JIT编译版本与手写优化版本的性能几乎持平,有时甚至更快。这表明,借助AI,过去因编写汇编代码难度过高而鲜少被采用的JIT编译器,如今已变得触手可及。这为构建更高效的软件(如pgrust数据库)打开了新的可能性。
评论总结
以下是对评论内容的总结,关注主要观点、论据及平衡性,并保留关键引用:
1. 对pgrust项目的兴趣与可行性担忧 - 观点:pgrust项目有趣,但深度修改导致无法向上游合并,其最终目标是否足够稳健以获得广泛采用存疑。 - 关键引用:glenjamin: "pgrust sounds very interesting, but with the deep changes there’s no viable path to upstream it"
2. JIT编译的安全性与可控性争议 - 观点:有评论认为JIT编译不安全(roschdal: "JIT compilation is unsecure"),但另有人指出在Common Lisp中JIT不仅可用且可控,程序员可决定编译内容(glum64: "Common Lisp, where JIT is not only available but is also manageable")。
3. 对PostgreSQL JIT性能的讨论 - 观点:PostgreSQL基于LLVM的JIT编译生成代码耗时较长,但JIT编译器并不罕见,许多主流解释器和PCRE2都使用JIT。存在比LLVM更快的代码生成框架(如Cranelift、GNU Lightning),但可能仍不如自定义的copy-and-patch JIT快。 - 关键引用:MaxBarraclough: "There's no rarity of JITs... Every major interpreter has a JIT compiler... I doubt they could do code-generation faster than a custom copy-and-patch JIT"
4. 对pgrust技术路线的批评 - 观点:pgrust使用的copy-and-patch编译并非真正的JIT编译,仅是带基本替换的汇编模板,且因未使用LLVM而缺失其优化能力。 - 关键引用:mgaunard: "it's not real JIT-compilation, it's just assembly templates with basic substitutions... you're missing all the optimizations it does"
5. JIT编译器开发难度的不同看法 - 观点:有评论认为JIT编译器历史上因实现困难而罕见,但另有人指出这仅适用于从头编写,现有框架已降低门槛。JIT编译器在特定领域(如正则引擎)仍是代码编写的难点。 - 关键引用:varjag: "writing the code absolutely was the hard part. JIT compilers are a great example of that"