苹果用 LLVM JIT 提升 WebKit JS 引擎的速度

  • Abel Avram
  • 马德奎

2014 年 5 月 21 日

话题:JavaScript性能调优语言 & 开发架构

通过将 JavaScript 转换成 LLVM IR 代码并在随后对其进行重量级优化,苹果将 Safari JavaScript 引擎 Nitro 的速度提升了 35%。

根据webkit.org 上的一篇博文,对内部的 JavaScript 字节码,WebKit 以前有三个级别的优化,每个级别都是在运行时使用,依据是要在优化代码段所需的时间和这样做的好处之间取得平衡:

  • LLInt(Low Level Interpreter)——这是一个字节码解释器而不是编译器,它在优化方面做的工作很少。每个函数调用都会通过 LLInt 处理,如果它包含一个被调用超过了 100 次的语句,或者函数本身被调用超过 6 次,那么它会被传递到下个优化级别,即 Baseline JIT。
  • Baseline JIT——这是一个简单的 JIT,创建的代码执行速度比 LLInt 快,但它不包含重量级优化。同样,如果发现一个语句执行超过 1000 次,或者一个函数被调用超过 66 次,那么编译会传递到下一个优化级别,即 DFG JIT。借助“栈上替换(On-Stack Replacement,OSR)”,可以在运行一条语句后切换编译器。
  • DFG JIT(Data Flow Graph JIT)——直到现在,该编译器负责 Safari 的性能,但只用于需要更多 CPU 的代码段,因为代码优化需要时间。

为了进一步提升 Nitro 的性能,苹果决定将LLVM引入优化链。Chris Lattner是 LLVM 编译器基础架构的原作者和负责人,他为苹果工作,领导着开发工具部门,苹果此举可能是由他推动的。这个第四层名为 FTL JIT(Forth Tier LLVM),它是一个C++ 模块,最终使用 LLVM 进行低级别优化。为此,一个函数的字节码通过两个中间阶段转换成 LLVM IR——“连续传递风格(Continuation-Passing Style)”(CPS)和“静态单一赋值(Static Single Assignment)”(SSA)——旨在将原先的动态代码转换优化成静态代码,并随后使用 LLVM 编译器进行处理。这是 LLVM 第一次用于一门动态语言的剖析制导编译,该做法需要在 LLVM 中进行若干深层次的修改,据 Filip Pizlo 说

将 LLVM JIT 基础架构用于一门动态语言的剖析制导编译,WebKit FTL JIT 是第一个这样做的重点项目。为此,我们需要在 WebKit 和 LLVM 中做若干重大修改。与我们现有的 JIT 相比,LLVM 编译代码所需的时间明显更多。WebKit 使用了一个复杂的分代垃圾收集器,但 LLVM 并不支持侵入式垃圾收集算法。性能剖析驱动的编译意味着我们可能在函数运行时调用一个优化编译器,而且我们可能在一个循环的中间将函数的执行转换到经过优化的代码;据我们了解,FTL 是第一个为将“热循环(hot-loop)”转换到经 LLVM 编译的代码做栈上替换的编译器。最后,LLVM 先前不支持自修改代码和反优化技巧,而我们依赖它们处理动态代码。

苹果做这方面的工作已有一年,他们取得了什么成果呢?据 Pizlo 说,Safari 在 Richards 基准测试中比 DFG 快 38%,在若干 asm.js 基准测试中平均快 35%。与 Chrome 或者 Firefox 上的Octane基准测试相比如何,尚有待观察。Pizlo 补充说“有关 FTL 的工作才刚刚开始——我们仍然需要扩大 FTL 能够编译的 JavaScript 操作集,它还有性能潜力可挖。”

FTL JIT 刚刚提交到 WebKit 主干,变更集编号为167958,读者可以使用 WebKit 每日构建版对其进行测试。

查看英文原文:Apple Speeds Up WebKit’s JS Engine with LLVM JIT

JavaScript性能调优语言 & 开发架构