v8 const,let和var的JavaScript性能影响?

时间:2016-10-16 13:12:21

标签: javascript performance const v8 let

无论功能差异如何,使用新关键字'让'和' const'对' var'?

的表现有任何普遍或具体的影响

运行程序后:

function timeit(f, N, S) {
    var start, timeTaken;
    var stats = {min: 1e50, max: 0, N: 0, sum: 0, sqsum: 0};
    var i;
    for (i = 0; i < S; ++i) {
        start = Date.now();
        f(N);
        timeTaken = Date.now() - start;

        stats.min = Math.min(timeTaken, stats.min);
        stats.max = Math.max(timeTaken, stats.max);
        stats.sum += timeTaken;
        stats.sqsum += timeTaken * timeTaken;
        stats.N++
    }

    var mean = stats.sum / stats.N;
    var sqmean = stats.sqsum / stats.N;

    return {min: stats.min, max: stats.max, mean: mean, spread: Math.sqrt(sqmean - mean * mean)};
}

var variable1 = 10;
var variable2 = 10;
var variable3 = 10;
var variable4 = 10;
var variable5 = 10;
var variable6 = 10;
var variable7 = 10;
var variable8 = 10;
var variable9 = 10;
var variable10 = 10;

function varAccess(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += variable1;
        sum += variable2;
        sum += variable3;
        sum += variable4;
        sum += variable5;
        sum += variable6;
        sum += variable7;
        sum += variable8;
        sum += variable9;
        sum += variable10;
    }
    return sum;
}

const constant1 = 10;
const constant2 = 10;
const constant3 = 10;
const constant4 = 10;
const constant5 = 10;
const constant6 = 10;
const constant7 = 10;
const constant8 = 10;
const constant9 = 10;
const constant10 = 10;

function constAccess(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += constant1;
        sum += constant2;
        sum += constant3;
        sum += constant4;
        sum += constant5;
        sum += constant6;
        sum += constant7;
        sum += constant8;
        sum += constant9;
        sum += constant10;
    }
    return sum;
}


function control(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
    }
    return sum;
}

console.log("ctl = " + JSON.stringify(timeit(control, 10000000, 50)));
console.log("con = " + JSON.stringify(timeit(constAccess, 10000000, 50)));
console.log("var = " + JSON.stringify(timeit(varAccess, 10000000, 50)));

..我的结果如下:

ctl = {"min":101,"max":117,"mean":108.34,"spread":4.145407097016924}
con = {"min":107,"max":572,"mean":435.7,"spread":169.4998820058587}
var = {"min":103,"max":608,"mean":439.82,"spread":176.44417700791374}

然而,此处所述的讨论似乎表明在某些情况下性能差异的真正潜力:https://esdiscuss.org/topic/performance-concern-with-let-const

5 个答案:

答案 0 :(得分:73)

TL; DR

理论上,这个循环的未优化版本:

for (let i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

比使用var的同一循环的未优化版本慢:

for (var i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

因为使用i为每个循环迭代创建了不同的 let变量,而只有一个i带有var

在实践中,在2018年,V8对循环进行了足够的内省,以了解何时可以优化差异。 (甚至在此之前,可能是你的循环正在做足够的工作,以至于额外的let相关的开销无论如何都会被淘汰。但现在你甚至不用担心它。)

详细

var循环中letfor之间的重要区别在于为每次迭代创建了不同的i;它解决了经典的“闭环问题”:

function usingVar() {
  for (var i = 0; i < 3; ++i) {
    setTimeout(function() {
      console.log("var's i: " + i);
    }, 0);
  }
}
function usingLet() {
  for (let i = 0; i < 3; ++i) {
    setTimeout(function() {
      console.log("let's i: " + i);
    }, 0);
  }
}
usingVar();
setTimeout(usingLet, 20);

为每个循环体(spec link)创建新的EnvironmentRecord是有效的,并且工作需要时间,这就是理论上let版本比var版本慢的原因。< / p>

但是,如果你在使用i的循环中创建一个函数(闭包),那么区别才重要,就像我在上面的runnable片段示例中所做的那样。否则,无法观察到这种区别,可以对其进行优化。

在2018年,看起来V8(和Firefox中的SpiderMonkey)正在做足够的内省,在没有使用let的每次迭代变量语义的循环中没有性能成本。请参阅this jsPerf test

在某些情况下,const可能会提供var不会优化的机会,尤其是全局变量。

全局变量的问题在于它是全局的; 任何代码 可以访问它。因此,如果您使用var声明一个您永远不打算更改的变量(并且永远不会对您的代码进行更改),那么引擎就不能假设它永远不会因为稍后加载的代码或类似代码而发生变化。 / p>

但是,对于const,您明确告诉引擎该值不能更改¹。因此可以自由地进行任何所需的优化,包括使用它发出文字而不是代码的变量引用,知道值无法更改。

¹请记住,对于对象,值是对象的引用,而不是对象本身。因此,对于const o = {},您可以更改对象的状态(o.answer = 42),但不能使o指向新对象(因为这需要更改对象引用它包含)。

在其他let类情况下使用constvar时,他们可能不会有不同的表现。无论您使用var还是let,此函数都应具有完全相同的性能,例如:

function foo() {
    var i = 0;
    while (Math.random() < 0.5) {
        ++i;
    }
    return i;
}

当然,这一切都不太重要,只有在需要解决的时候才能解决这个问题。

答案 1 :(得分:5)

T.J。人群的答案非常好。

这里是对以下内容的补充:“何时将现有的var声明编辑为const才能获得最大收益?”

我发现性能的最大提升与“导出”功能有关。

因此,如果文件A,B,R和Z正在调用文件U(通常通过您的应用程序使用)中的“实用程序”函数,则将该实用程序函数切换为“ const”,并将父文件引用切换为const可以消除一些改进的性能。在我看来,它的速度并没有明显提高,但是对于我的单片科学怪人版应用而言,整体内存消耗却减少了1-3%。如果您要在云或裸机服务器上花费大量现金,那么花30分钟进行梳理并将其中一些var声明更新为const可能是一个很好的理由。

我意识到,如果您读懂const,var的内容,并在幕后工作的话,您可能已经得出了上述结论……但是如果您“瞥了一眼”它,则:D。

根据我记得在进行更新时在节点v8.12.0上进行基准测试的情况,我的应用程序从约240MB RAM的空闲消耗变为了约233MB RAM。

答案 2 :(得分:5)

“让”更适合于循环声明

在导航器中进行如下简单测试(5次):

// WITH VAR
console.time("var-time")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var-time")

平均执行时间超过2.5毫秒

// WITH LET
console.time("let-time")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let-time")

平均执行时间超过1.5毫秒

我发现let的循环时间更好。

答案 3 :(得分:0)

T.J。人群的答案很好,但:

  1. “让”使代码更具可读性,而不是更强大
  2. 理论上,let会比var慢
  3. 通过实践,编译器无法完全解决(未完成的静态分析)未完成的程序,因此有时它将错过优化
  4. 在任何情况下,使用“ let”进行内省都需要更多的CPU,必须在Google v8开始解析时启动工作台
  5. 如果内省失败,则“ let”将对V8垃圾收集器产生巨大影响,它将需要更多迭代才能释放/重用。它也会消耗更多的RAM。替补席上必须考虑到这些要点
  6. Google Closure将转换let变量...

var和let之间的性能差距的影响可以在现实的完整程序中看到,而不是在单个基本循环上看到。

无论如何,在不需要的地方使用let会使代码的可读性降低。

答案 4 :(得分:0)

带有 'let' 的代码将比带有 'var' 的代码更优化,因为当作用域到期时,用 var 声明的变量不会被清除,但用 let 声明的变量会被清除。所以 var 使用更多空间,因为它在循环中使用时会产生不同的版本。