为什么在生成器中调用函数运行更快?

3

function* test() {
  console.time("function-call")
  loop();
  console.timeEnd("function-call");

  console.time("in-function");
  var i, j;
  i = 0;
  while (i < 10000) {
    j = 0;
    while (j < 10000) {
      j++
    }
    i++;
  }
  console.timeEnd("in-function");
}

function loop() {
  var i, j;
  i = 0;
  while (i < 10000) {
    j = 0;
    while (j < 10000) {
      j++
    }
    i++;
  }
}

test().next();

我将“循环”中的代码块复制并粘贴到“测试”函数中,以比较时间。

function-call: 84ms 
in-function: 596ms

调用函数比在函数内部循环要快得多。为什么呢?

2
我的猜测是优化器意识到你的循环函数什么也没做(ij在循环外无法访问,循环内也没有任何操作),因此被优化掉了。但出于某种原因,当循环处于更大的上下文中时,同样的优化并没有生效。 - Joachim Sauer
在Chrome(v8引擎)中,in-function函数稍微快一些。在Firefox中,我重现了您的结果。有趣的问题。 - Christian Vincenzo Traina
@JoachimSauer 实际上,我有类似的代码块可以做某些事情。我准备了一个简化版本来询问。 - Cory
在 Node.js 上测试函数调用:154.183 毫秒,在函数内部:152.907 毫秒,@Cory,请附加您的原始代码。 - Daniel
1
有趣的是,将in-function函数封装在IIFE中运行速度更快。所以@JoachimSauer的猜测可能是100%正确的。也许我们不应该问为什么function-call更快,而应该问为什么in-function更慢。 - Christian Vincenzo Traina
显示剩余2条评论
1个回答

1

@Cristian Traìna Node 不允许跳过空循环。这些优化只允许在编译型语言中使用,例如 Pascal 或带有类似 -O2 标志的 C/C++。

对于此程序

var max=process.argv[2];
for(var i=0;i<=max;i++){} // with let results is the same

我们可以从循环次数得到执行时间的依赖关系。这是一个LogLog图表。第一个平坦区域是NodeJs开始占据主导执行时间的领域。在1M次循环后,您可以看到迭代次数的增加与时间呈线性比例关系。最高测量时间约为1000秒,因此V8肯定不会跳过空循环。
(来源:gustawdaniel.pl) 回到问题的脚本:
  • Chrome 71.0.3578.98
function-call: 154.878662109375ms
in-function: 153.7490234375ms
  • Node v10.15.0
function-call: 154.183ms
in-function: 152.907ms
  • Firefox Quantum 64.0
function-call: 156 ms debugger eval code:4:3
in-function: 1519 ms
  • Chromium 71.0.3578.98
function-call: 158.954345703125ms
in-function: 153.663818359375ms
  • Vivaldi 2.2
function-call: 153.548095703125ms
in-function: 153.755126953125ms
  • Opera 58.0.3135.47
function-call: 154.34814453125ms
in-function: 154.729248046875ms

我朋友的电脑测试结果如下:
  • Edge

注意:该文本包含HTML标签,已保留。
function-call: 3 496,6 ms
in-function: 2 330,9 ms
  • Chrome
function-call: 70.69580078125ms
in-function: 70.43310546875ms

现在看来,Firefox和Edge存在问题。

除了Firefox和Edge之外的任何浏览器都使用V8引擎。Firefox在这里描述:

https://www.digitaltrends.com/web/mozilla-firefox-new-browser-engine-quantum-2017/

并使用 Quantum Flow

Edge团队正在考虑采用Chromium引擎。

https://www.pcmag.com/news/365345/microsofts-edge-browser-to-adopt-googles-chromium-engine

从这篇文章中

https://blog.mozilla.org/blog/2018/12/06/goodbye-edge/

我们可以看到,未来只支持来自谷歌的Chromium和来自Mozilla的Gecko Quantum。
如果有人能够访问Safari或Edge,请添加测试。

1
好的,我从未说过V8跳过空循环,但即使我没有说,它仍然可能是真的。即使JavaScript不是一种编译语言,最现代的引擎也会执行一些即时编译,并预测和优化代码。如果我没记错的话,V8至少使用两个线程,一个用于优化,一个用于执行。 - Christian Vincenzo Traina
我同意“你没有说过”和它有可能是真的。我观察到只有在编译语言中,并且只有在特殊标志的情况下才会跳过空循环。 - Daniel
我的意思是,由于Node的速度与PHP、Perl、Python、Ruby等许多脚本语言中的空循环的典型速度相同,因此Node在此任务中并没有特别优化。这是一个图表,显示了执行一个循环而不使用此特殊标志的时间对数(较小的值表示更快)https://blog.gustawdaniel.pl/content/images/2019/01/speed-2.png - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接