发布 SunSpider 1.0

流行的 SunSpider JavaScript 基准测试套件最初由 WebKit 团队于五年多前,即2007年12月发布。它旨在平衡来自网络的真实 JavaScript 代码,并作为 WebKit JavaScript 引擎“应该”但尚未能够优化的语言级操作的蓝图。而我们确实做到了优化:有趣的是,基准测试的原始版本报告称在 2.33 GHz 机器上的 Safari 3 中执行时间为 9206.2 毫秒。在现代浏览器(如 Safari 6)和最新机器(例如我的 2.2GHz i7 MacBook Pro)上运行 SunSpider 0.9 基准测试,很容易获得约 250 毫秒的分数——这代表了超过 30 倍的提升!

尽管取得了如此显著的性能提升,并且引入了诸如 KrakenOctane 等其他套件,SunSpider 仍然是一个有用的基准测试。SunSpider 专注于广泛的 JavaScript 操作,从 Date、String 和 Regexp 操作,到各种数值、数组导向、面向对象和函数式编程风格。与其他套件相比,SunSpider 更强调使 JavaScript 特别难以优化的特性,例如 'eval' 和 'for/in'。最重要的是,SunSpider 是一个简单实用的测试,不仅测试 JavaScript 代码在运行一段时间后能执行多快,还测试引擎对于运行时间很短的代码能多快地“热启动”。大多数网站没有持续运行几秒钟的 JavaScript 事件处理程序;因此,SunSpider 对短时运行测试的关注与网络直接相关。

今天,我们很高兴宣布推出 SunSpider 1.0,这是一个套件更新,修复了许多错误,旨在进一步提高测试的准确性和可重复性。本次更新包括对测试验证的修复、与电源管理的更好交互,以及对测试框架和测试本身的大量小修补。

测试验证

SunSpider 最初编写时,JavaScript 引擎使用的是相对直接的解释或模板编译策略。因此,测试没有必要防御可能导致整个测试被作为死代码折叠掉的优化编译器技巧。验证测试的输出也没有太大意义——如果你的执行策略只是解释,那么你可以使用其他方法(例如 test262)来验证引擎的正确性,而基准测试只需衡量执行时间。

但如今的 JavaScript 运行时采用了日益复杂的执行层级。例如,WebKit 的 JavaScript 引擎有三个执行引擎:用于启动代码的解释器、用于多态代码的模板即时编译器 (JIT),以及用于长时间运行的结构化代码的优化 JIT。JavaScript 的优化编译器越来越擅长消除死代码:已知其结果永远不会被使用的代码。但更重要的是,随着引擎复杂度不断提高,让我们的基准测试执行一些正确性验证变得有意义。基准测试在压力测试优化 JIT 方面具有独特性,一致性测试无法做到这一点。这一观察来自我们自身的经验:在我们为基准测试添加验证后,我们能够更快地捕获错误。

我们通过在 26 个 SunSpider 测试中的 23 个中添加验证检查来解决这两个问题。验证检查旨在对测试运行时间产生最小的开销:我们的目标是开销 <2%。未进行验证的测试结果取决于时区随机数生成;对于这些测试,我们预计不同的用户和实现会得到不同的结果。我们也不对 Math.sin() 等某些数学函数的精确结果进行验证,因为ECMAScript 5 允许这些函数返回依赖于实现的结果。

这些验证检查有两个作用:它们强制 JavaScript 引擎完整执行测试,并提供了一种快速检查引擎是否正确执行测试的方法。

添加测试验证和防止死代码消除的更改涉及 WebKit 的 bug 3844663863114852114895

电源管理

SunSpider 中使用的短时运行测试需要测试框架的特别注意。SunSpider 1.0 版本通过消除以前版本在测试执行之间使用的延迟,进一步提高了测试执行时间的可重复性,从而减少了操作系统电源管理逻辑干扰的可能性。

最初的 SunSpider 0.9 测试框架在测试之间采用了 500 毫秒的延迟。其目的是让浏览器有机会在进行下一轮测量之前完成任何异步活动。但性能得到了提升,现代操作系统中的电源管理功能也变得更加复杂。在测试执行之间采用 500 毫秒延迟运行一个平均耗时 10 毫秒的测试,意味着机器可以在每次测试执行时进入较低时钟频率的省电状态。如果电源管理介入,机器就会运行得更慢,SunSpider 分数就会受到影响。电源管理是否介入取决于框架无法控制的多种因素。矛盾的是,这反而会导致较慢或较嘈杂的机器表现出更高的性能。较慢的机器在测试时间片中活跃的时间占比较高,从而降低了电源管理干扰的可能性。较嘈杂的机器——例如在 SunSpider 测试进行时同时进行其他工作的机器——也有机会获得更好的结果,因为噪音也会抑制电源管理。

SunSpider 0.9.1 更新试图通过将测试延迟从 500 毫秒减少到 10 毫秒来解决此问题。但随着 SunSpider 性能因硬件改进和 JavaScript 引擎增强的结合而进一步提升,我们再次看到电源管理导致 SunSpider 不可预测地变慢。例如,在 2.7GHz MacBook Pro 上进行测试有时会导致比 2.2GHz MacBook Pro 更差的结果,并不是因为硬件更差或浏览器有何不同,而是因为更快的机器在时间片段中活跃的时间占比看起来更小。更糟的是,更快的机器并非持续运行较慢;我们观察到双峰执行时间,根据机器状态在 130 毫秒和 150 毫秒之间波动。

在快速硬件上出现双峰结果与我们将 SunSpider 打造成可靠且可重复测试的目标背道而驰。我们考虑了解决此问题的各种方案。我们可以让 SunSpider 测试运行更长时间,但我们拒绝了这种方法,因为 SunSpider 的独特优势之一就是其短时运行的特性;此外,其他 JavaScript 基准测试已经在长时间运行的程序测试覆盖方面做得很好。我们还考虑过要求用户在运行 SunSpider 之前禁用电源管理;这虽然可行,但会使测试更难使用。最终,我们选择完全移除 10 毫秒的测试延迟。最初设置测试延迟的目的是为了改善 SunSpider 的可重复性和可靠性,而不是抑制它。在尝试不同延迟时,我们发现完全消除延迟消除了快速硬件上的双峰结果,并且在我们测试的任何硬件平台上都没有显著损害测试的可重复性。

SunSpider 1.0 消除了测试执行之间的延迟。除了减少电源管理设施的干扰外,它还有另外两个有用的副作用

  1. SunSpider 测试套件现在运行速度最高可提升一倍。典型的 SunSpider 测试在快速机器上运行所需时间不到 10 毫秒,因此测试执行之间 10 毫秒的延迟意味着 SunSpider 的大部分运行时间都花在了空闲等待上。移除这个延迟意味着测试完成得更快,因为单个测试之间等待的时间减少了。
  2. SunSpider 现在减少了浏览器在测试框架中隐藏异步活动的机会。我们的目标是尽可能多地测量 JavaScript 引擎的开销。在 <10 毫秒的测试运行之间设置 10 毫秒的延迟,意味着一个积极地将垃圾回收 (GC) 活动推迟到空闲时间的浏览器,其 GC 开销将永远不会被基准测试测量到。将 GC 活动推迟到空闲时间是一种有据可查的性能提升技术,但我们认为 SunSpider 的目标不是专门奖励浏览器内存管理策略的这一方面。GC 性能很重要,我们认为 SunSpider 应该倾向于测量它而不是忽略它。

当电源管理生效时,改善测试可重复性的更改涉及 bug 114458

测试框架的小调整

SunSpider 1.0 还包括命令行和浏览器内测试框架中的一些额外修复和功能

  • bug 47045:测试框架实际上没有 close() 其测试文档。
  • bug 60937:避免使用未声明的变量 'name',它是 DOM API。
  • bug 71799:扩展 SunSpider 驱动程序,使其能够运行 Kraken。
  • bug 80783:为 SunSpider 添加 –instruments 选项以使用 Instruments 进行性能分析。

试试看吧!

我们邀请您试用我们新的 SunSpider 1.0 基准测试套件,报告您的发现,并在遇到意外问题时提供反馈。虽然不推荐,但仍然可以从所有版本页面运行旧版本的测试框架。