Bli*_*n67 23 javascript performance google-chrome let ecmascript-6
考虑到Chrome主要版本尚未发布,Chrome Canary 59 的新型Ignition + Turbofan引擎已经解决了这个问题.测试显示相同的时间let和var声明的循环变量.
原始(现在是静音)问题.
当使用let在forChrome上循环运行速度非常缓慢,相比于移动变量外刚内循环的范围.
for(let i = 0; i < 1e6; i ++);
Run Code Online (Sandbox Code Playgroud)
需要两倍的时间
{ let i; for(i = 0; i < 1e6; i ++);}
Run Code Online (Sandbox Code Playgroud)
到底是怎么回事?
Snippet演示了差异,只影响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()Run Code Online (Sandbox Code Playgroud)
当我第一次遇到这个时,我认为这是因为新创建的i实例,但以下显示并非如此.
请参阅代码片段,因为我已经消除了使用ini随机优化附加let声明的任何可能性,然后添加到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()Run Code Online (Sandbox Code Playgroud)
T.J*_*der 23
更新: 2018年6月:Chrome现在比这个问题和答案首次发布时更好地优化了这一点; 如果你没有在循环中创建函数,那么在使用let中不会有任何明显的损失for(如果你是,那么这些好处是值得的).
因为i为循环的每次迭代创建了一个new ,所以在循环中创建的闭包关闭了该i 迭代的闭包.这由用于评估for循环体的算法中的规范涵盖,其描述了每循环迭代创建新的变量环境.
例:
for (let i = 0; i < 5; ++i) {
setTimeout(function() {
console.log("i = " + i);
}, i * 50);
}
// vs.
setTimeout(function() {
let j;
for (j = 0; j < 5; ++j) {
setTimeout(function() {
console.log("j = " + j);
}, j * 50);
}
}, 400);Run Code Online (Sandbox Code Playgroud)
那是更多的工作.如果 请参阅上面的更新,除了边缘情况之外无需避免它.i每个循环不需要new ,请let在循环外部使用.
我们可以预期,现在除了模块之外的所有东西都已经实现了,V8可能会改进新东西的优化,但是功能应该首先优先于优化并不奇怪.
很高兴其他引擎已经完成了优化,但V8团队显然还没有到达那里.见上面的更新.
考虑到Chrome主要版本还没有针对Chrome Canary 60.0.3087 的新型Ignition + Turbofan 引擎解决了这个问题.测试显示相同的时间let和var声明的循环变量.
边注.我的测试代码Function.toString()在Canary上使用和失败,因为它"function() {"不像"function () {"以前的版本那样返回(使用regexp轻松修复)但对于那些使用它的人来说是一个潜在的问题Function.toSting()
更新感谢用户Dan.中号谁提供的链接https://bugs.chromium.org/p/v8/issues/detail?id=4762(和单挑),其中有更多的问题.
以前的答案
Optimiser选择退出.
这个问题让我困惑了一段时间,这两个答案是明显的答案,但由于时间差太大而无法创建新的范围变量和执行上下文,因此没有任何意义.
为了证明这一点,我找到了答案.
简短的回答
优化器不支持声明中带有let语句的for循环.
Chrome版本55.0.2883.35测试版,Windows 10.
一张价值千言万语的图片,应该是第一个看的地方.
上述个人资料的相关功能
var time = [0,0]; // hold total times
function letInside(){
var start = performance.now();
for(let i = 0; i < 1e5; i += 1); // <- if you try this at home don't forget the ;
time[0] += performance.now()-start;
setTimeout(letOutside,10);
}
function letOutside(){ // this function is twice as quick as test on chrome
var start = performance.now();
{let i; for(i = 0; i < 1e5; i += 1)}
time[1] += performance.now()-start;
setTimeout(displayResults,10);
}
Run Code Online (Sandbox Code Playgroud)
由于Chrome是主要的参与者,并且循环计数器的阻止范围变量无处不在,那些需要高性能代码并认为块范围变量很重要的人function{}(for(let i; i<2;i++}{...})//?WHY?应该考虑暂时替代语法并在循环外声明循环计数器.
我想说的是时间差是微不足道的,但是考虑到函数中的所有代码都没有经过优化使用for(let i...应该谨慎使用.