【发布时间】:2017-03-19 20:53:47
【问题描述】:
重大更新。
Chrome Canary 59 的新 Ignition+Turbofan engines 尚未在 Chrome 主要版本中解决该问题。测试显示 let 和 var 声明的循环变量的时间相同。
原来的(现在没有实际意义的)问题。
当在 Chrome 上的 for 循环中使用 let 时,与将变量移动到循环范围之外相比,它的运行速度非常慢。
for(let i = 0; i < 1e6; i ++);
需要两倍的时间
{ let i; for(i = 0; i < 1e6; i ++);}
发生了什么事?
片段展示了差异,并且只影响 Chrome,而且只要我记得 Chrome 支持let,它就一直如此。
var times = [0,0]; // hold total times
var count = 0; // number of tests
function test(){
var start = performance.now();
for(let i = 0; i < 1e6; i += 1){};
times[0] += performance.now()-start;
setTimeout(test1,10)
}
function test1(){
// this function is twice as quick as test on chrome
var start = performance.now();
{let i ; for(i = 0; i < 1e6; i += 1);}
times[1] += performance.now()-start;
setTimeout(test2,10)
}
// display results
function test2(){
var tot =times[0]+times[1];
time.textContent = tot.toFixed(3) + "ms";
time1.textContent = ((times[0]/tot)*100).toFixed(2) + "% " + times[0].toFixed(3) + "ms";
time2.textContent = ((times[1]/tot)*100).toFixed(2) + "% " + times[1].toFixed(3) + "ms";
if(count++ < 1000){;
setTimeout(test,10);
}
}
var div = document.createElement("div");
var div1 = document.createElement("div");
var div2 = document.createElement("div");
var time = document.createElement("span");
var time1 = document.createElement("span");
var time2 = document.createElement("span");
div.textContent = "Total execution time : "
div1.textContent = "Test 1 : "
div2.textContent = "Test 2 : "
div.appendChild(time);
div1.appendChild(time1);
div2.appendChild(time2);
document.body.appendChild(div);
document.body.appendChild(div1);
document.body.appendChild(div2);
test2()
当我第一次遇到这种情况时,我以为是因为新创建的 i 实例,但以下显示并非如此。
请参阅代码 sn-p,因为我已经消除了附加的 let 声明被 ini 随机优化然后添加到 k 的不确定值的任何可能性。
我还添加了第二个循环计数器p
var times = [0,0]; // hold total times
var count = 0; // number of tests
var soak = 0; // to stop optimizations
function test(){
var j;
var k = time[1];
var start = performance.now();
for(let p =0, i = 0; i+p < 1e3; p++,i ++){j=Math.random(); j += i; k += j;};
times[0] += performance.now()-start;
soak += k;
setTimeout(test1,10)
}
function test1(){
// this function is twice as quick as test on chrome
var k = time[1];
var start = performance.now();
{let p,i ; for(p = 0,i = 0; i+p < 1e3; p++, i ++){let j = Math.random(); j += i; k += j}}
times[1] += performance.now()-start;
soak += k;
setTimeout(test2,10)
}
// display results
function test2(){
var tot =times[0]+times[1];
time.textContent = tot.toFixed(3) + "ms";
time1.textContent = ((times[0]/tot)*100).toFixed(2) + "% " + times[0].toFixed(3) + "ms";
time2.textContent = ((times[1]/tot)*100).toFixed(2) + "% " + times[1].toFixed(3) + "ms";
if(count++ < 1000){;
setTimeout(test,10);
}
}
var div = document.createElement("div");
var div1 = document.createElement("div");
var div2 = document.createElement("div");
var time = document.createElement("span");
var time1 = document.createElement("span");
var time2 = document.createElement("span");
div.textContent = "Total execution time : "
div1.textContent = "Test 1 : "
div2.textContent = "Test 2 : "
div.appendChild(time);
div1.appendChild(time1);
div2.appendChild(time2);
document.body.appendChild(div);
document.body.appendChild(div1);
document.body.appendChild(div2);
test2()
【问题讨论】:
-
第一个必须在每次迭代中创建一个新级别的范围,如果它没有被优化以避免它。
-
我认为在早期,
let的语义将与您的第二个示例相匹配,其中头部中的let声明的范围为围绕整个循环的块。每种方法各有利弊。 -
不确定为什么最后一个示例保持其性能特征。也许他们看到
j在该范围内的单个突变之外从未使用过,因此他们优化了添加/分配。甚至可能是声明,当出于某种原因在该位置声明时。谁知道。需要推测,除非熟悉实现的代码库。 -
@squint 我再次修改以消除第二个让优化的可能性。 (见sn-p)让我明白的是,额外的时间比我所看到的任何解释所造成的要多得多。认为是时候咬紧牙关看看代码库了。
标签: javascript performance google-chrome ecmascript-6 let