在 Microsoft Edge 提供快速的 JavaScript 性能
我们已经在 Windows 10 和 Microsoft Edge 大大地提高了 Chakra JavaScript 引擎的性能。目的是尽可能地利用现有硬件,让你的 JavaScript 代码跑的更快。以便你创建的网页能在 Microsoft Edge 中提供更好的用户体验。在开发过程中,我们团队的目标之一,就是仔细观察用户反馈回来的数据,尽可能的让在现有的网页上面跑的更快些。
就在不久前, 我们曾经为 Microsoft Edge 提供,在 Windows 10 中提高 Chakra JavaScript 引擎性能的各种方案。 改善性能是永无止尽的追求,所以在本文中,我们来谈一谈,如何根据当今的网页反馈回来的数据提高 Chakra’s JIT 编译器的性能。
跨文件脚本内联
现在的 JavaScript 编译器都把内联函数当作是优化性能的关键因素。函数内联是指把被调用的函数体插入调用的函数当中,就好像被调用的函数直接写在调用的函数代码中一样,因此,能节省函数调用和返回的开销(比如,寄存器的保存与恢复)。对于那些对性能要求较高的代码,函数内联可以提高20-30%的性能。
在内联过程中, 编译器会自动权衡是否要内联处理。 比如,有些时候,编译器为了生成内联代码,而花在收集上下文信息上的时间,比优化过后省下来的时间还要多。或者,试图内联其他上下文或脚本文件的函数,付出的代价远比从内联得到的要高。
在开发 Windows 10 和 Microsoft Edge 过程中, 我们从现有的网站收集了一些数据, 以便更好的理解 Chakra 的内联优化效果。我们从排名前 10000 的网站中, 随机选取 f 3,000 个, 得出如下数据:
在 Chakra中只有 30% 的函数调用进行内联。 而有 48% o的函数, 由于调用与被调用分别存放在不同的脚本文件中, 无法内联。 | 在另外一个图表中,超过 60% 的网站没有内联。 |
在 Windows 10 和 Microsoft Edge 中, Chakra’s JIT 编译器和和执行管道已经优化过, 所以, Chakra 现在能够高效地内联跨脚本文件的函数, 而不会丢失过多的性能。这种方式, 允许现有网站的 JavaScript 代码在 Microsoft Edge 上运行的更快。
通过固定字段提高全局常量速度
ECMAScript6 将 const 常量值 引入到了 Javascript 语言中。Const 常量在给 Javascript 开发者带来语言和工具的便利的同时,还使得 Javascript 编译器可以优化查询的新能。当一个属性被定义为常量,编译器可以该属性在程序的整个生命周期都是不会发生改变的。在这个前提下,编译器可以相应的优化,从而避免在这种属性的查询开销。查询开销包括了检查该属性的类型, 结构,内部表现,找到该属性实际储存的值,以及检查在程序的执行过程该值是否发生过变化等一系列开销。而对于常量,编译器不用执行以上任何一种检查。
然后常量在网站正在日益增加,但是现存的大多数网站都还没有试用常量结构。对于现在的网站,多数的常量都是被定义为一个全局变量,然后在代码中到处试用. 在我们对10,000多家网站进行的一种实验中,我们发现20%的网站都有定义整形常量的现象。而且每家网站有平均超过4处这种做法。
在 Windows 10 和 Microsoft Edge 中,我们优化了 Chakra 的解析器和 JIT 编译器,用以识别出声明的非常数整型变量,这些变量在程序运行期间都有定义,但是没有变化。一旦鉴别出来,Chakra 的 JIT 编译器生成的代码就可以大幅度地减小循环消耗,因为这样的全局变量在整个程序运行期间它们的值和形状都没有发生改变。因此将面向性能的 ECMAScript 6 中关于常数声明的价值主张延伸到今天常数是如何广泛地应用在网络中。
提升 try-catch 块中代码的性能
在今天,使用 try-catch 是非常普遍的。但是,在实践中并不推荐使用 try-catch,尤其对于那些对性能很敏感的代码。Try-catch 代码很难被优化,因为 try-catch 块中的大对数操作会导致异常,然后被捕获。这种方式使 JIT 编译器很难得到一个精准的模型。不同的技术需要使用不同的模型,这就导致执行引擎需要创建额外的开销来维护不同的模型。
我们所做的数据采集实验是基于4500个热门站点的,就此了解有超过96%的站点会抛出JavaScript异常。实际上,超过50%的站点会抛出超过10个以上的JavaScript异常。
直到Windows 10,Chakra都没有优化try-catch块内的代码。在Windows 10和Microsoft Edge中, Chakra的编译器现在可以抽象try-catch代码块内的代码和JIT优化的代码。这种情况下异常不会被抛出,Chakra现在执行try块内的代码几乎与普通的JIT代码一样(这就好像try-catch不存在一样)。
Minified JS代码现在带来大小和速度的好处
现今的 Web 通常都使用了 minified JavaScript 代码,这带来了一个优势,即减少了客户端(浏览器)显示内容的大小。在 Windows10 release 版本中,在调查一个特殊的性能问题的时候,我们发现一件事情,一些使用 minified JS 代码(minified JS 使用 UglifyJS)的实例,性能不及未使用 minified JS 的实例。在某些情况下,开发者在使用 minified JS 的过程中,使用了一些我们认为开发者通常不会使用的代码模式,这也是 Chakra 还未做优化的原因。于是,我们做了一个快速的试验,用来查看 Web 上使用 minified JS 的情况。我们从 10,000 个站点中随机采样了 4,000 个站点,下面是我们发现的信息:
Minification on Top 4000 sites | ||
95% of the sites had some form of minified code | Out of the 95%, 77% sites had some code that was minified using UglifyJS | Out of the 95%, 47% of the sites used jQuery minified via UglifyJS |
这个试验确认了,在 Web 中 minified JS 代码的使用非常流行,由于 UglifyJS 存在于其它代码之中,所以它也非常广泛的应用于现今的 Web 之中。因此,在 Windows10 和 Microsoft Edge 浏览器之中,我们增加了用来提升内联的新途径。同时,我们优化了在 Chahra 的 JIT 编译器的一些探索法,用来确保 minified JS 代码运行的尽可能和没有使用 minified JS 代码的版本一样快--即便不比它们更快。基于这些改变,我们测试过,使用 UglifyJS 的单一代码模式的 minified JS 性能提升了大概20-50%。
Array#indexOf 优化
在Web中使用数组的情况是非常普遍的。除了提供polyfills和帮助函数,许多非官方的JavaScript库,尝试着提供JavaScript语言一些标准数组内建函数的更快的实现。理想的情况下,对于内建的部分,所有的浏览器都应该足够的快,这样,库就可以更集中于提供polyfill和帮助API,而不是担心在不同浏览器之间修复内建部分的性能问题。另一方面,开发者也不应该,仅仅为了让一些所有JavaScript引擎实现的基本内建部分运行的更快,而不得不使用一个库。
尽管我们离上面提到的理想情况很远,在最近的一个数据采集的试验中,我们尝试着估量在现今Web中使用最多的ECMAScript 5 的内建部分。这个试验从10,000个站点中随机采样大约4000个站点。我们发现,其中使用最多的前三名分别是: Array#indexOf, Array#map 和 Array#forEach。
鉴于Array内建函数在Web中的广泛使用,在Windows10 和 Microsoft Edge中,当引擎遍历一个数组时,Chakra优化了获取值的过程。当数组中存在“洞”(hole,即不存在元素)的时候,这项优化有助于去除访问原型链(extraneous overhead)和根据序号查找数值的时候的外部开销。这项优化提升了Chakra和Microsoft Edge中内建的 ECMAScript5 Array#indexOf 函数超过5倍的性能。
那么,我们已经够快了吗?
在下面列出来的许多优化中都是来自于网络上面已经存在的数据,帮助站点在 Microsoft Edge 上面运行的更快。我们不愿意去讨论关于虚拟测试,还是经常被问及(Charkra)是如何在 Mircorsoft Edge 上获得如此高的 Javascript 测试性能的。下面的图是我们目前为止已经提交的IE11同其他流行的浏览器相比在 Microsoft Edge 上得到性能加强的 Javascript 测试结果。
所有的测试基准都是在64位Window 10 技术预览版上面运行的64位浏览器收集而得.
系统信息:HP Compaq 8100 Elite with Intel(R) Core(TM) i7
CPU 860 @ 2.80GHz (4 cores), 12GB RAM
这张图说明了什么?在 Microsoft Edge 上 Charkra 比 IE11 更快。仔细看,Chakra 在以下的这些的测试中通过 Microsoft Edge 获得了性能提升:
Benchmark Microsoft Edge 的性能改进超过了 IE11
Jet Stream Apple 超过了1.5倍
Octane 2.0 Google 超过了2.25倍
注意:你会好奇在这个性能测试中为什么是了 64 位浏览器而不是 32 位浏览器,原因是不像 IE11 Microsoft Edge 需要运行在 64 位的平台下。所有的流行的 64 位 Javascript 引擎相比较 32 位平台运行有一点点慢,选择 64 位平台可以提供一些安全特性,在这篇博客中得到了补充。
然而在赢得了跑分中并没有使我们感到很满意,关键是为了提升 Javascript 的性能从 IE11 开始 Microsoft Edge 已经走过了很长的一段路程,就像测试已经存在了。如我们开始提到的,性能是一个不懈的追求。我们会继续挖掘 Javascript 在 Microsoft Egde 中得性能极限。请继续保持反馈帮助我们提升。您可以在网站上提交 bug,在用户之声上提交反馈,或者在 推ter 上面的 @MSEdgeDev 为我们提供援助。
– Gaurav Seth, Principal PM Lead, Chakra