简体   繁体   English

"const、let 和 var 对 v8 JavaScript 性能的影响?"

[英]v8 JavaScript performance implications of const, let, and var?

Regardless of functional differences, does using the new keywords 'let' and 'const' have any generalized or specific impact on performance relative to 'var'?不管功能上的差异如何,使用新关键字“let”和“const”相对于“var”对性能有什么普遍或具体的影响吗?

After running the program:运行程序后:

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)));

.. My results were the following: ..我的结果如下:

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}

However discussion as noted here seems to indicate a real potential for performance differences under certain scenarios: https://esdiscuss.org/topic/performance-concern-with-let-const然而,这里提到的讨论似乎表明在某些情况下性能差异的真正潜力: https ://esdiscuss.org/topic/performance-concern-with-let-const

TL;DR TL; 博士

In theory , an unoptimized version of this loop:理论上,此循环的未优化版本:

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

might be slower than an unoptimized version of the same loop with var :可能比带有var的相同循环的未优化版本慢:

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

because a different i variable is created for each loop iteration with let , whereas there's only one i with var .因为不同的i变量与每个循环迭代创建let ,而只有一种是ivar

Arguing against that is the fact the var is hoisted so it's declared outside the loop whereas the let is only declared within the loop, which may offer an optimization advantage.与此相反的事实是var被提升,因此它在循环外声明,而let仅在循环内声明,这可能提供优化优势。

In practice , here in 2018, modern JavaScript engines do enough introspection of the loop to know when it can optimize that difference away.实际上,在 2018 年,现代 JavaScript 引擎对循环进行了足够的内省,以了解何时可以优化这种差异。 (Even before then, odds are your loop was doing enough work that the additional let -related overhead was washed out anyway. But now you don't even have to worry about it.) (即使在此之前,您的循环很可能已经做了足够多的工作,以至于额外的与let相关的开销无论如何都被冲掉了。但现在您甚至不必担心它。)

Beware synthetic benchmarks as they are extremely easy to get wrong, and trigger JavaScript engine optimizers in ways that real code doesn't (both good and bad ways).当心合成基准测试,因为它们非常容易出错,并且会以真实代码不会的方式(好的和坏的方式)触发 JavaScript 引擎优化器。 However, if you want a synthetic benchmark, here's one:但是,如果您想要一个综合基准,这里有一个:

 const now = typeof performance === "object" && performance.now ? performance.now.bind(performance) : Date.now.bind(Date); const btn = document.getElementById("btn"); btn.addEventListener("click", function() { btn.disabled = true; runTest(); }); const maxTests = 100; const loopLimit = 50000000; const expectedX = 1249999975000000; function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) { console.log(`Running Test #${index} of ${maxTests}`); setTimeout(() => { const varTime = usingVar(); const letTime = usingLet(); results.usingVar += varTime; results.usingLet += letTime; console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`); ++index; if (index <= maxTests) { setTimeout(() => runTest(index, results), 0); } else { console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`); console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`); btn.disabled = false; } }, 0); } function usingVar() { const start = now(); let x = 0; for (var i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; } function usingLet() { const start = now(); let x = 0; for (let i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; }
 <input id="btn" type="button" value="Start">

It says that there's no significant difference in that synthetic test on either V8/Chrome or SpiderMonkey/Firefox.它说在 V8/Chrome 或 SpiderMonkey/Firefox 上的综合测试没有显着差异。 (Repeated tests in both browsers have one winning, or the other winning, and in both cases within a margin of error.) But again, it's a synthetic benchmark, not your code. (在两种浏览器中的重复测试都会有一个获胜,或者另一个获胜,并且在这两种情况下都在误差范围内。)但同样,它是一个综合基准,而不是您的代码。 Worry about the performance of your code when and if your code has a performance problem.当您的代码出现性能问题时,请担心您的代码性能。

As a style matter, I prefer let for the scoping benefit and the closure-in-loops benefit if I use the loop variable in a closure.作为一个风格问题,如果我在闭包中使用循环变量,我更喜欢let的作用域优势和闭包中的优势。

Details细节

The important difference between var and let in a for loop is that a different i is created for each iteration;for循环中varlet之间的重要区别在于,每次迭代都会创建不同的i it addresses the classic "closures in loop" problem:它解决了经典的“循环闭包”问题:

 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);

Creating the new EnvironmentRecord for each loop body ( spec link ) is work, and work takes time, which is why in theory the let version is slower than the var version.为每个循环体( 规范链接)创建新的 EnvironmentRecord 是可行的,而且工作需要时间,这就是为什么理论上let版本比var版本慢的原因。

But the difference only matters if you create a function (closure) within the loop that uses i , as I did in that runnable snippet example above.但是,只有在使用i的循环中创建函数(闭包)时,差异才重要,就像我在上面的可运行代码段示例中所做的那样。 Otherwise, the distinction can't be observed and can be optimized away.否则,无法观察到差异并且可以将其优化掉。

Here in 2018, it looks like V8 (and SpiderMonkey in Firefox) is doing sufficient introspection that there's no performance cost in a loop that doesn't make use of let 's variable-per-iteration semantics.在 2018 年,看起来 V8(和 Firefox 中的 SpiderMonkey)正在做足够的内省,在不使用let 's variable-per-iteration 语义的循环中没有性能成本。 See this test .请参阅此测试


In some cases, const may well provide an opportunity for optimization that var wouldn't, especially for global variables.在某些情况下, const可能会提供var不会提供的优化机会,尤其是对于全局变量。

The problem with a global variable is that it's, well, global;全局变量的问题在于它是全局的。 any code anywhere could access it.任何地方的任何代码可以访问它。 So if you declare a variable with var that you never intend to change (and never do change in your code), the engine can't assume it's never going to change as the result of code loaded later or similar.因此,如果您使用var声明一个永远不打算更改的变量(并且永远不会更改您的代码),则引擎不能假设它永远不会因为稍后加载的代码或类似的结果而更改。

With const , though, you're explicitly telling the engine that the value cannot change¹.但是,使用const ,您明确地告诉引擎该值不能更改¹。 So it's free to do any optimization it wants, including emitting a literal instead of a variable reference to code using it, knowing that the values cannot be changed.所以它可以自由地进行任何它想要的优化,包括发出一个文字而不是使用它的代码的变量引用,知道这些值不能改变。

¹ Remember that with objects, the value is a reference to the object, not the object itself. ¹ 请记住,对于对象,值是对对象引用,而不是对象本身。 So with const o = {} , you could change the state of the object ( o.answer = 42 ), but you can't make o point to a new object (because that would require changing the object reference it contains).因此,使用const o = {} ,您可以更改对象的状态( o.answer = 42 ),但不能使o指向新对象(因为这需要更改它包含的对象引用)。


When using let or const in other var -like situations, they're not likely to have different performance.在其他类似var的情况下使用letconst时,它们不太可能具有不同的性能。 This function should have exactly the same performance whether you use var or let , for instance:无论您使用var还是let ,此函数都应具有完全相同的性能,例如:

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

It's all, of course, unlikely to matter and something to worry about only if and when there's a real problem to solve.当然,这一切都不重要,只有当有真正的问题需要解决时才需要担心。

"LET" IS BETTER IN LOOP DECLARATIONS “让”在循环声明中更好

With a simple test (5 times) in navigator like that:在导航器中进行简单的测试(5 次),如下所示:

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

The mean time to execute is more than 2.5ms平均执行时间超过2.5ms

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

The mean time to execute is more than 1.5ms平均执行时间超过1.5ms

I found that loop time with let is better.我发现使用 let 的循环时间更好。

TJ Crowder 's answer is so excellent. TJ Crowder的回答非常出色。

Here is an addition of: "When would I get the most bang for my buck on editing existing var declarations to const ?"这里有一个补充:“在将现有的 var 声明编辑为 const 时,我什么时候才能获得最大的收益?”

I've found that the most performance boost had to do with "exported" functions.我发现最大的性能提升与“导出”函数有关。

So if file A, B, R, and Z are calling on a "utility" function in file U that is commonly used through your app, then switching that utility function over to "const" and the parent file reference to a const can eak out some improved performance.因此,如果文件 A、B、R 和 Z 正在调用文件 U 中通常通过您的应用程序使用的“实用程序”函数,则将该实用程序函数切换为“const”并且父文件对 const 的引用可以出一些改进的性能。 It seemed for me that it wasn't measurably faster, but the overall memory consumption was reduced by about 1-3% for my grossly monolithic Frankenstein-ed app.对我来说,它似乎并没有明显更快,但是对于我非常单一的 Frankenstein-ed 应用程序,整体内存消耗减少了大约 1-3%。 Which if you're spending bags of cash on the cloud or your baremetal server, could be a good reason to spend 30 minutes to comb through and update some of those var declarations to const.如果您在云或裸机服务器上花费大量现金,那么花 30 分钟梳理并将其中一些 var 声明更新为 const 可能是一个很好的理由。

I realize that if you read into how const, var, and let work under the covers you probably already concluded the above... but in case you "glanced" over it :D.我意识到,如果您深入了解 const、var 和 let 的工作原理,您可能已经得出了上述结论……但万一您“瞥了一眼”:D。

From what I remember of the benchmarking on node v8.12.0 when I was making the update, my app went from idle consumption of ~240MB RAM to ~233MB RAM.根据我在进行更新时对节点 v8.12.0 的基准测试的记忆,我的应用程序从大约 240MB RAM 的空闲消耗变为了大约 233MB RAM。

TJ Crowder's answer is very good but : TJ Crowder 的回答很好,但是:

  1. 'let' is made to make code more readable, not more powerful 'let' 是为了使代码更具可读性,而不是更强大
  2. by theory let will be slower than var理论上 let 会比 var 慢
  3. by practice the compiler can not solve completely (static analysis) an uncompleted program so sometime it will miss the optimization通过实践编译器不能完全解决(静态分析)一个未完成的程序,所以有时它会错过优化
  4. in any-case using 'let' will require more CPU for introspection, the bench must be started when google v8 starts to parse在任何情况下使用“让”将需要更多的 CPU 进行内省,必须在 google v8 开始解析时启动工作台
  5. if introspection fails 'let' will push hard on the V8 garbage collector, it will require more iteration to free/reuse.如果内省失败,'let' 将大力推动 V8 垃圾收集器,它将需要更多的迭代来释放/重用。 it will also consume more RAM.它也会消耗更多的内存。 the bench must take these points into account替补席必须考虑到这些要点
  6. Google Closure will transform let in var... Google Closure 将在 var 中转换 let...

The effect of the performance gape between var and let can be seen in real-life complete program and not on a single basic loop. var 和 let 之间性能差距的影响可以在现实生活中的完整程序中看到,而不是在单个基本循环中。

Anyway, to use let where you don't have to, makes your code less readable.无论如何,在不需要的地方使用 let 会降低代码的可读性。

As @Amn's test concludes, using let in a single for() loop is better.正如@Amn 的测试得出的结论,在单个for()循环中使用 let 更好。 I achieved the same results after doing the same test.我做了同样的测试后得到了同样的结果。

console.time("let")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let") // let: 2.8828125 ms

console.clear()

console.time("var")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var") // 3.93896484375 ms

// let is faster

However, I have also tried using multiple let s in different loops .但是,我也尝试在不同的循环中使用多个let

Multiple i variables多个i变量

console.time("var");
for(var i = 0; i < 10; i++)
 for(var i = 0; i < 10; i++)
  for(var i = 0; i < 10; i++)
   for(var i = 0; i < 10; i++);
console.timeEnd("var"); // var: 0.02734375 ms

console.clear()

console.time("let");
for(let i = 0; i < 10; i++)
 for(let i = 0; i < 10; i++)
  for(let i = 0; i < 10; i++)
   for(let i = 0; i < 10; i++);
console.timeEnd("let"); // let: 0.221923828125 ms

// var is faster

After this test, using let was on average 10 times slower.在这个测试之后,使用let平均慢了 10 倍。

Different variable names不同的变量名

console.time("var");
for(var i = 0; i < 10; i++)
 for(var j = 0; j < 10; j++)
  for(var k = 0; k < 10; k++)
   for(var l = 0; l < 10; l++);
console.timeEnd("var"); // var: 0.639892578125 ms

console.clear()

console.time("let");
for(let i = 0; i < 10; i++)
 for(let j = 0; j < 10; j++)
  for(let k = 0; k < 10; k++)
   for(let l = 0; l < 10; l++);
console.timeEnd("let"); // let: 0.180908203125 ms

// let is faster

There are advantages and disadvantages to both.两者都有优点和缺点。 Personally, I prefer let .就个人而言,我更喜欢let


for...of

I also tested all three in a for...of loop.我还在for...of循环中测试了所有三个。 I used this test:我使用了这个测试:

var arr = Array(1000).fill(0)
for(let i = 00; i < 100; i++){
  console.time("const")
  for(const e of arr);
  console.timeEnd("const")
}

I tried for(const...of) , for(let...of) , and for(var...of) .我试过for(const...of)for(let...of)for(var...of) I took the average of all the times.我取了所有时间的平均值。

  • let : 0.023911133 ms let :0.023911133 毫秒
  • var : 0.023691406 ms var功:0.023691406 毫秒
  • const : 0.020488281 ms const :0.020488281 毫秒

The results of let and var are pretty similar. letvar的结果非常相似。 Even though var performed 0.00022 ms faster, I think it's safe to say that in this situation, it doesn't matter which one you use.尽管var执行速度快了 0.00022 毫秒,但我认为可以肯定地说,在这种情况下,您使用哪个并不重要。 As for const , it is clear that it is slightly faster.至于const ,很明显它稍微快一点。

So, when using for...of loops, I recommend using const But keep in mind, using const...of does not allow variable manipulation (but you probably already knew that).因此,当使用for...of循环时,我建议使用const但请记住,使用const...of不允许变量操作(但您可能已经知道了)。

just did some more tests and it seems that there is a substantial difference in favor of var<\/strong> .只是做了一些更多的测试,似乎有利于var<\/strong>的实质性差异。 My results show that between Const \/ Let \/ Var there is a ratio from 4 \/ 4 \/ 1 to 3 \/ 3 \/ 1. I give the code i used below.我的结果表明,在 Const \/ Let \/ Var 之间存在一个从 4 \/ 4 \/ 1 到 3 \/ 3 \/ 1 的比率。我给出了我在下面使用的代码。 Just let me mention that i started from the code of AMN<\/a> and did lots of tweaking.让我提一下,我从AMN<\/a>的代码开始,做了很多调整。 I did the tests both in w3schools_tryit editor and in Google_scripts My Notes:我在 w3schools_tryit 编辑器和 Google_scripts 我的笔记中都做了测试:

code with 'let' will be more optimized than 'var' as variables declared with var do not get cleared when the scope expires but variables declared with let does.带有“let”的代码将比带有“var”的代码更优化,因为当作用域到期时,用 var 声明的变量不会被清除,但用 let 声明的变量会被清除。 so var uses more space as it makes different versions when used in a loop.所以 var 使用更多的空间,因为它在循环中使用时会产生不同的版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM