在 5μs 内 JIT 编译代码

数据库的 JIT 编译正在借助超快的“复制并补丁”方法重新受到关注,这类方法能在微秒级生成机器码,避免 LLVM 的高延迟,同时仍可相对解释执行获得显著加速。评论者在权衡这些性能收益与安全担忧:严格 W^X 策略被放宽的风险、JIT 的复杂性和攻击面(尤其是在处理不受信任输入时),以及只有在特定领域内 JIT 才值得其额外的可移植性与维护成本。一些人还提到更轻量的 JIT 框架、Common Lisp 中的手动 JIT 技术,甚至 LLM 辅助代码生成,作为让专用 JIT 更可行的方式。

JIT 与 W^X 的安全性

  • 一方认为 JIT 天生不那么安全,因为它需要可写且可执行的内存,削弱了严格的 W^X 策略,并使更强大的漏洞利用成为可能。
  • 其他人则反驳说,现代 JIT 通常会分配 RW 页面,写入代码,然后将其切换为 RX,因此每个映射仍然遵守 W^X。
  • 争论点在于这是否具有“系统范围的影响”,还是只是每个进程各自的加固选择;有人说操作系统本来就必须管理这类权限,另一些人则强调,允许动态代码执行会增加漏洞的影响范围。
  • 讨论中举出了对 JIT 严格限制的平台示例(iOS、GrapheneOS),以及通过字节码验证和硬件内存标记让 JIT 更安全的技术。
  • 几条评论指出,JIT 会增加宿主进程的攻击面(尤其是在处理不受信任输入时,例如浏览器或数据库查询),但不会赋予进程超出其原本能力的新权限。
  • 还提到了 ROP 和类似技术,用来论证如果某个进程能够执行任意代码,那么无论有没有 JIT,实际上都已经可以实现任意代码执行。

JIT 设计、性能,以及 LLVM 与轻量方案

  • 多条评论强调,基于 LLVM 的 JIT 延迟很高,不适合需要极快编译的工作负载(例如 Postgres 查询)。
  • 轻量级 JIT 方法——复制并补丁模板、小型库(例如 SLJIT、GNU Lightning、AsmJit),或自定义后端——以更少的优化换取更低的编译时间。
  • 链接文章中的基准测试显示,简单 JIT 能在几乎不耗编译时间的情况下,相对解释执行获得明显加速,而 LLVM 可能会花费几十毫秒来编译。
  • 面向数据库的评论强调,查询计划优化(例如连接顺序)远比 JIT 更重要;Postgres 目前的 JIT 粒度(按表达式而不是按流水线)被视为一个根本性限制。

基于模板的代码生成算不算“真正”的 JIT?

  • 有人将这种做法斥为“只是汇编模板”,但也有人反驳说:
    • 不做优化的编译器仍然是编译器。
    • 复制并补丁(copy-and-patch)是标准且合法的 JIT 技术。
  • 有例子说明,极其简单的代码生成器(没有寄存器分配,大量使用栈)虽然只比高度优化的 -O3 二进制慢几倍,但仍然比解释器快得多。

Common Lisp 与手动 JIT

  • 讨论将 Common Lisp 实现视为一种中间方案:
    • 有些会急切地编译所有代码;另一些则支持字节码加可选的本地编译。
    • eval 和加载时编译被视为一种 JIT 形式。
    • 还有切换解释器和编译器的开关,便于 REPL 使用,在编译时间和执行速度之间做权衡。
  • 一种“手动 JIT”风格——明确选择哪些内容要编译以提升速度——被称赞为一种实用且更简单的折中方案。

AI 在编写 JIT 和复杂系统中的角色

  • 观点明显分化:
    • 有人说当前模型在复杂领域里生成的代码很差,主要适用于样板代码、CRUD、API 接线和简单的 bug 查找。
    • 也有人表示,借助 AI 搭建复杂组件(包括 JIT)能显著提升生产力,只要有能力的工程师来拆解任务并审查输出,就能把一周的工作缩短到几个小时。
  • 争论集中在:
    • 仍然需要多少领域知识(很多人说“很多”)。
    • AI 生成的代码质量究竟是客观上很差,还是只是风格不同。
    • 非专家用户发布表面上不错但实际上脆弱的 AI 代码所带来的风险。

其他应用与观察

  • 有评论建议将类似基于模板的 JIT 思路用于防火墙和即时生成 eBPF。
  • 正则表达式引擎、历史上的 BASIC 和 Lisp 系统,以及现代 DBMS,都被提到是经典或自然适合 JIT 的领域。
  • 讨论串还简要提到了形式化验证的解释器和签名的静态二进制,作为 JIT 和原生代码的极端安全替代方案。