Javascript 为什么运行这个循环9次比运行8次要长100倍?

Javascript 为什么运行这个循环9次比运行8次要长100倍?,javascript,performance,firefox,Javascript,Performance,Firefox,考虑以下代码: Test = function() { } t = new Test(); for (var i = 0; i < 8; i++) { result = t instanceof Test; } Test=函数(){ } t=新测试(); 对于(变量i=0;i

考虑以下代码:

Test = function() {
}

t = new Test();

for (var i = 0; i < 8; i++) {
  result = t instanceof Test;
}
Test=函数(){
}
t=新测试();
对于(变量i=0;i<8;i++){
结果=试验的t实例;
}
如果将迭代次数从8更改为9,则在最新版本的Firefox(41.0.1)中完成循环所需的时间将突然增加约100倍。我在两台不同的电脑上测试了这个,魔法极限总是8

下面是我使用的JSPerf测试:

有人知道为什么会发生这种情况吗?这似乎是特定于instanceof的。如果对对象执行其他操作,例如检查属性,则不会发生这种情况



注意:我也提交了一份关于这一点的报告。

当值为9时,循环10次,因此100x大概是10^2-即两个字符而不是一个字符。你也可以确定100次是否会导致10^3的减速。听起来很疯狂,但是这个Javascript。

来自Mozilla团队的Jan de Mooij在。以下是我对他的高度技术性答案的简单解释:

i<8
的情况下,Firefox足够聪明,可以提升
result=t instanceof Test语句脱离循环(根据我的测试,它似乎没有完全忽略它)。在
i<9
的情况下,显然不会进行优化

为什么??原因并不十分清楚,但可能与以下事实有关:9次迭代是阈值,超过该阈值,函数被认为“热”到足以通过JIT编译器运行。
i<8
案例保留在解释器中。(我不明白为什么JIT-ing会排除提升,但显然在当前版本的引擎中是这样的。)

有趣的是,8次迭代的阈值似乎并不普遍。例如,如果我们用内置原型(例如,
CustomEvent
)替换我们自己的原型(
Test
),那么无论迭代次数多少,提升似乎都不会发生():

Ion JIT优化编译器可以轻松优化我们在同一原型对象上多次使用
instanceof
(就像我们在中使用
CustomEvent
)的情况,但是当它注意到有多个同名对象时,显然它会将它的手抛向空中

Jan正确地指出,这不太可能影响太多的真实脚本,因为通常单个标识符与单个原型对象关联(例如,您有一个类
Foobar
,它只定义一次,从不重新定义)。但是JSPerf重新定义了数百次原型。在我看来,这一事实对所有已发布的JSPerf结果(包括原型定义)都产生了严重的怀疑,除了那些明确避免使用globals重新定义的结果(如本文所述)——这可能是所有这些结果中最重要的结论


例如,从这个问题:链接的JSPerf测试可能毫无价值,因为它们都在设置代码中定义原型。

有趣的是,当您内联8/9次时,它不会发生:。Firefox 40.3上也没有。在函数中进行递归循环显然更快。古怪。感谢您为此问题打开一个bug()。我的盲目猜测是,这只是编译基线编译器的成本,当我们额外进入循环一次时,基线编译器就会出现。如果你要使用更多的迭代次数,每次迭代的成本应该分摊。我在不同的Firefox版本(20.0、30.0、35.0、36.0、36.0.4和37.0)中尝试过。这似乎与版本37.0中添加的内容有关,比37更旧的版本运行良好。在哪里可以找到此版本的完整更改日志?无论循环有9次迭代还是9000次迭代,每次/迭代都差不多。这意味着原因可能不是每个循环执行一次昂贵的操作。如果是这样,9000迭代版本将花费更少的时间/迭代。然而,仍然有可能在每个测试中执行一次昂贵的操作(在测试中,循环被执行多次)。
for (var i = 0; i < 8; i++) { //or i < 9
  t instanceof CustomEvent;
}
Test = function(){
}