宣布推出 SquirrelFish

SquirrelFish Mascot

“你好,互联网!”

WebKit 的核心 JavaScript 引擎刚刚获得了一个新的解释器,代号 SquirrelFish。

SquirrelFish 速度很快——比 WebKit 以前的解释器快得多。看看这些数据。在 SunSpider JavaScript 基准测试中,SquirrelFish 比 WebKit 以前的解释器快 1.6 倍。

SunSpider 每分钟运行次数

bar graph of SunSpider runs

条形越长越好。

SquirrelFish 是什么?

SquirrelFish 是一个基于寄存器、直接线程、高级字节码引擎,采用滑动寄存器窗口调用约定。它使用一个简单的单趟编译器和内置的拷贝传播,从语法树中惰性生成字节码。

SquirrelFish 的设计很大程度上借鉴了高效虚拟机领域的一些最新研究,包括 M. Anton Ertl 教授及其团队、David Gregg 教授及其团队以及 Lua 编程语言开发人员所做的研究。

关于这些主题的一些很棒的入门读物包括

我还仔细研读了大量关于这些主题的糟糕书籍和论文。我就不费时间赘述那些了。

为什么它速度快

与许多脚本语言的解释器一样,WebKit 以前的 JavaScript 解释器是一个简单的语法树遍历器。要执行一个程序,它会首先将程序解析成语句和表达式的树。例如,表达式“x + y”可能会解析为

        +   
      /   \    
     x     y

创建语法树后,解释器会递归地访问树中的节点,执行它们的操作并传播执行状态。这种执行模型会产生几种运行时开销。

首先,语法树描述的是程序的语法结构,而不是执行程序所需的操作。因此,在执行过程中,解释器会重复访问那些没有做任何有用工作的节点。例如,对于代码块“{ x++; }”,解释器会首先访问块节点“{...}”,该节点不做任何操作,然后访问其第一个子节点,即增量节点“x++”,该节点对 x 进行增量操作。

其次,即使是执行有用工作的节点,其访问成本也很高。每次访问都需要一次虚函数调用和返回,这意味着需要两次间接内存读取来检索被调用的函数,以及两次间接分支——一次用于调用,一次用于返回。在现代硬件上,“间接”是“慢”的同义词,因为间接往往会破坏缓存和分支预测。

第三,为了在节点之间传播执行状态,解释器必须传递大量数据。例如,在处理涉及局部变量的子树时,解释器会在子树中的所有节点之间复制变量的值。因此,从表达式“f((x) + 1)”中的“x”部分开始,变量节点“x”会将 x 返回给括号节点“(x)”,后者又会将 x 返回给加法节点“(x) + 1”。然后,加法节点会将 (x) + 1 返回给参数列表节点“((x) + 1)”,该节点会将该值复制到一个参数列表对象中,然后将其传递给 f 的函数节点。天哪!

在我们的第一轮优化中,我们在不改变底层架构的情况下,尽可能地提升了性能。这样做使我们能够对编写的每个优化进行回归测试。它也为任何替代技术设定了非常高的标准。最终,在充分发挥了语法树架构的潜力后,我们转向了字节码。

SquirrelFish 的字节码引擎巧妙地消除了树遍历解释器几乎所有的开销。首先,字节码流精确地描述了执行程序所需的操作。编译成字节码会隐式地剥离不相关的语法结构。其次,字节码调度是单次直接内存读取,后跟单次间接分支。因此,执行字节码指令比访问语法树节点快得多。第三,语法树消失后,解释器不再需要在语法树节点之间传播执行状态。

字节码的寄存器表示和调用约定协同工作,也能带来其他加速。例如,跳到 JavaScript 函数的第一条指令,以前需要两次 C++ 函数调用(其中一次是虚调用),现在只需要一次字节码调度。同时,字节码编译器知道如何剥离多种形式的中间拷贝,通常可以安排在不进行任何拷贝的情况下将参数传递给 JavaScript 函数。

仅仅是个开始

在典型的编译器中,转换为字节码只是达到目的的一种手段,而不是目的本身。转换的目的是将抽象的语法构造树“降低”为具体的执行原语向量,后者形式更适合众所周知的优化技术。

因此,尽管我们对 SquirrelFish 目前的性能非常满意,但我们也相信这仅仅是个开始。既然我们有了字节码表示,我们正在研究的一些编译时优化包括:

  • 常量折叠
  • 更积极的拷贝传播
  • 类型推断——包括精确和推测性推断
  • 基于表达式上下文的专门化——特别是 void 和布尔上下文
  • 窥孔优化
  • 逃逸分析

这是一个有趣的问题领域。由于网络上许多脚本只执行一次就被丢弃,我们需要发明这些优化方法的简单高效版本。此外,由于 JavaScript 是一种动态语言,我们还需要发明这些优化方法的版本,使其在未知环境下具有弹性。

我们还在进一步优化虚拟机,包括:

  • 常量池指令
  • 超指令
  • 带有隐式寄存器操作数的指令
  • 高级调度技术,如指令复制和上下文线程
  • 让计算 goto 在 Windows 上工作

Windows 上的性能还有额外的提升空间,因为 Windows 上的解释器尚未实现直接线程。它使用循环内的 switch 语句代替计算 goto。

参与其中

如果您对编译器或虚拟机感兴趣,这是一个非常棒的项目。我们进展迅速,所以快速熟悉情况的最佳方式是登录我们的 IRC 频道

一如既往,测试 每夜构建版本报告错误 也是非常有帮助的。

额外奖励更新

我们有一些额外奖励信息:非常早期的 SquirrelFish VM 操作码的 草稿文档。对于那些了解 VM 的人来说,您可能会觉得这很有启发性;对于不了解的人来说,您可能会发现它比您预期的要简单。

此外,我们还提供了 Safari 3.1 与 SquirrelFish 的详细比较,查看各项测试,很有意思地可以看到哪些测试速度提升最多。如果您查看 与 Safari 3.0 的比较,您会发现自 Safari 3 以来,我们整体速度提升了 4.34 倍,某些类型的代码甚至提升了一个数量级以上。

网络上的 SquirrelFish:关于这篇帖子的 reddit 文章中有很多有趣的讨论。SquirrelFish 核心开发者 Cameron Zwarich 的帖子提供了性能数据和其他信息,WebKit 偶尔的贡献者 Charles Ying 也是如此。