[译]JavaScript引擎中的自动函数内联

原文:http://ariya.ofilabs.com/2013/04/automatic-inlining-in-javascript-engines.html


想当年,在JavaScript解释器的运行速度还很慢的时候,有一条最佳实践就是"不要在性能关键(performance-critical)的代码中使用函数调用".随着最近现代JavaScript引擎的不断发展,这条最佳实践变的不是那么必要了.其中,能在很大程度上减少我们对"频繁的函数调用会消耗过多性能"的担心的一个新特性就是自动函数内联(automatic function inlining).

考虑下面的代码:

function square(x) {
  return x * x;
}
 
function f(x) {
  var sum = 0;
  for (var i = 0; i < x; i++) {
    sum += square(i);
  }
  return sum;
}
如果x是一个比较大的数字,则执行f(x)的时候会非常多次的对square函数进行调用.即使该函数的单次调用的性能开销非常小,如果反复执行非常多次,这个性能开销也会变的非常显著.为此,就有了"不要在长循环体内进行函数调用"这样的一条最佳实践.

幸运的是,当这部分代码被执行的非常"热"(hot,指执行了非常多次)时,一个现代的JavaScript引擎能够感知到这一点并对其进行优化.在很多优化手段中,最简单的一条优化就是不要在每次循环中真正的调用square函数,而是直接对该函数进行内联(inline),因为这个函数非常简单,内联之后对代码的逻辑不会有任何影响.内联的意思就是说,你的代码会像下面这样的代码一样被执行:

function f(x) {
  var sum = 0;
  for (var i = 0; i < x; i++) {
    sum += i * i;
  }
  return sum;
}

怎么才能证明JavaScript引擎真的会对上述代码执行内联优化呢?下面是一个可行的证明方法:使用V8 debugger shell.首先,为了确保引擎会对我们的代码进行优化(代码能够变得非常"热"),我们再上述代码的基础上再加上这么一行:

for (var x = 0; x < 10000; ++x) f(1e4);
现在,如果你编译过V8并且手头已经有了V8 debugger shell(d8.exe),在命令行上执行下面的代码:
d8 --trace-inlining sum.js
就会输出这样一条信息(也可能还会有其他信息):
Inlined square called from f.

这就证明了V8最终决定不去反复的执行函数调用,还是去内联这个函数.

如果你以前经常会在给浏览器执行代码之前手动的进行函数内联操作,现在是时候改变这一做法了.你尽管考虑可维护性,即便把代码分成若干个很小的函数也没关系!在大多数情况下,我们可以相信现代的JavaScript引擎能够自动的对那些小函数进行内联优化.

posted @ 2013-04-13 11:57  紫云飞  阅读(2132)  评论(0编辑  收藏  举报