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

sea*_*078 66 javascript performance v8 const let

无论功能差异如何,使用新关键字"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)));
Run Code Online (Sandbox Code Playgroud)

..我的结果如下:

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}
Run Code Online (Sandbox Code Playgroud)

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

T.J*_*der 84

TL; DR

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

for (let i = 0; i < 500; ++i) {
    doSomethingWith(i);
}
Run Code Online (Sandbox Code Playgroud)

会比同一个循环的未经优化的版本慢var:

for (var i = 0; i < 500; ++i) {
    doSomethingWith(i);
}
Run Code Online (Sandbox Code Playgroud)

因为不同的 i变量与每个循环迭代创建let,而只有一个ivar.

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

细节

之间的重要差异varlet在一个for循环是一个不同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);
Run Code Online (Sandbox Code Playgroud)

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

但是,如果您在使用的循环中创建一个函数(闭包),那么差异才会很重要i,就像我在上面的runnable片段示例中所做的那样.否则,无法观察到这种区别,并且可以将其优化掉.

在2018年,看起来V8(以及Firefox中的SpiderMonkey)正在做足够的内省,在没有使用let每次迭代变量语义的循环中没有性能成本.看到这个jsPerf测试.


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

全局变量的问题在于它是全局的; 任何地方的代码可以访问它.因此,如果您声明一个var您永远不打算更改的变量(并且永远不会更改您的代码),那么引擎就不能假设它永远不会因为稍后加载的代码或类似代码而改变.

有了const,但是,你要明确地告诉该值不能change¹发动机.因此可以自由地进行任何所需的优化,包括使用它发出文字而不是对代码的变量引用,因为知道这些值无法更改.

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


在使用letconst在其他var情况下,他们可能不会有不同的表现.您是否使用这个功能应该具有完全相同的性能varlet,例如:

function foo() {
    var i = 0;
    while (Math.random() < 0.5) {
        ++i;
    }
    return i;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 截至2018年中期,带有let和var的版本在Chrome中具有相同的速度,所以现在没有任何区别了. (2认同)

小智 9

“ LET”更好地体现在循环声明中

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

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

平均执行时间超过2.5毫秒

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

平均执行时间超过1.5毫秒

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

  • 在Firefox 65.0中运行它,我得到的平均速度为“ var = 138.8ms”和“ let = 4ms”。那不是打字错误,`let`现在快30倍以上 (3认同)
  • 我刚刚在Node v12.5中进行了试用。我发现平均速度为“ var = 2.6ms”和“ let = 1.0ms”。因此,让Node快一倍以上。 (3认同)
  • 只是为了表达通常的观点,即在存在优化器的情况下性能测试很困难:我认为 let 循环正在被完全优化 - let 只存在于块内,并且循环没有副作用,而且 V8 足够聪明,知道它可以只需删除块,然后删除循环即可。var 声明已被提升,因此它无法知道这一点。你的循环,我得到 1ms/0.4ms,但是,如果对于这两个循环,我在循环之外有一个变量 j(var 或 let),它也递增,那么我得到 1ms/1.5ms。即 var 循环没有变化,let 循环现在花费更长的时间。 (3认同)
  • **注意综合基准**,特别是带有空体循环的基准。如果你实际上在循环中做了一些事情,[这个综合基准测试](https://jsben.ch/IWC6v)(再次注意!:-))表明没有显着差异。我还在我的答案中添加了一个,因此它是现场的(不像那些在我身上不断消失的 jsPerf 测试。:-))。重复运行表明一个获胜,或另一个获胜。当然没有什么定论。 (3认同)

isa*_*dre 6

TJ Crowder的回答是如此出色。

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

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

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

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

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