好奇的JavaScript性能取决于变量范围 [英] Curious JavaScript performance dependent on variable scope

查看:63
本文介绍了好奇的JavaScript性能取决于变量范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在测试一个JavaScript项目的性能时,我注意到一种非常特殊的行为 - JavaScript成员访问性能似乎受到它们所在范围的严重影响。我写了一些性能测试,结果因多个数量级。

When testing the performance of one JavaScript project, I noticed a very peculiar behavior - JavaScript member access performance seems to be heavily influenced by the scope they are in. I wrote a few performance tests, and the results were different by multiple orders of magnitude.

我使用这些浏览器在Windows 10 64位上进行了测试:

I tested on Windows 10 64-bit, using these browsers:

  • Google Chrome, version 49.0.2623.75 m - uses the V8 JavaScript engine
  • Mozilla Firefox, version 44.0.2 - uses the SpiderMonkey JavaScript engine
  • Microsoft Edge, version 25.10586 - uses the Chakra JavaScript engine

以下是我运行的最相关测试及其各自的测试结果:

Here are the most relevant tests I ran and their respective results:

// Code running on global scope, accessing a variable on global scope
// Google Chrome:   63000 ms.
// Mozilla Firefox: 57000 ms.
// Microsoft Edge:  21000 ms.
var begin = performance.now();
var i;
for(i = 0; i < 100000000; i++) { }
var end = performance.now();
console.log(end - begin + " ms.");


// Code running on local scope, accessing a variable on global scope
// Google Chrome:   61500 ms.
// Mozilla Firefox: 47500 ms.
// Microsoft Edge:  22000 ms.
var begin = performance.now();
var i;
(function() {
    for(i = 0; i < 100000000; i++) { }
})();
var end = performance.now();
console.log(end - begin + " ms.");

// Code running on local scope, accessing a variable on local scope
// Google Chrome:   50 ms.
// Mozilla Firefox: 28 ms.
// Microsoft Edge:  245 ms.
var begin = performance.now();
(function() {
    var i;
    for(i = 0; i < 100000000; i++) { }
})();
var end = performance.now();
console.log(end - begin + " ms.");

在本地范围和全局范围内运行的代码之间的差异在误差范围内, Firefox确实在本地范围内实现了相当稳定的20%性能提升

The difference between code running in local and global scopes was within the margin of error, although Firefox did seem to get a pretty consistent 20% performance boost running in local scope.

最大的惊喜是访问本地范围的变量,它是在Chrome和Firefox上 1200到1600倍,在Edge上快了90倍。

The biggest surprise was accessing a variable on a local scope, it was 1200 to 1600 times faster on Chrome and Firefox, and 90 times faster on Edge.

为什么会出现这种情况,在三种不同的浏览器上/ JavaScript引擎?

Why would this be the case, on three different browsers / JavaScript engines?

推荐答案

您可以看到V8 JavaScript引擎生成的实际机器代码(与Chrome中使用的相同)在 Node.js 下运行您的代码并传递 - print_opt_code 打开节点命令行。例如,如果您将代码放在名为 test.js 的文件中,则可以运行:

You can see the actual machine code generated by the V8 JavaScript engine (same as used in Chrome) by running your code under Node.js and passing the --print_opt_code switch on the node command line. For example if you put your code in a file called test.js you can run:

node --print_opt_code test.js

在上一个例子中,V8能够将 i 变量放在 RAX 寄存器中,而不是将其保存在内存中。这是上面命令打印出的代码的内部循环,带有一些额外的注释。 (之前和之后还有其他代码;这只是内部循环本身。)

In your last example, V8 is able to put the i variable in the RAX register instead of keeping it in memory. Here's the inner loop from the code printed out by the above command, with some extra notes. (There is additional code before and after; this is just the inner loop itself.)

 84  33c0           xorl rax,rax                 ; i = 0
 86  3d00e1f505     cmp rax, 0x5f5e100           ; compare i with 100000000
 91  0f8d12000000   jge 115                      ; exit loop if i >= 100000000
 97  493ba548080000 REX.W cmpq rsp, [r13+0x848]  ; check for bailout?
104  0f8246000000   jc 180                       ; bailout if necessary
110  83c001         addl rax, 0x1                ; i++
113  ebe3           jmp 86                       ; back to top of loop
115  ...

请注意 0x5f5e100 100000000 以十六进制表示。

Note that 0x5f5e100 is 100000000 represented in hexadecimal.

如您所见,这是一个只有少量指令的相当紧凑的循环。大多数代码都是JavaScript代码的直接翻译;我唯一不确定的是地址97和104处的两条指令,如果满足某个条件,就会退出循环。

As you can see, this is a fairly tight loop with only a few instructions. Most of the code is a direct translation of the JavaScript code; the only thing I'm a bit unsure about are the two instructions at addresses 97 and 104 that bail out of the loop if a certain condition is met.

如果你运行的话与其他版本的JavaScript代码进行类似的测试,您将看到更长的指令序列。请注意,Node将所有代码包装在它提供的包装函数中。因此,如果你想做你的第一个例子,你可能需要像这样编写循环以获得类似的效果:

If you run similar tests with your other versions of JavaScript code you will see much lengthier instruction sequences. Just be aware that Node wraps all of your code inside a wrapper function that it provides. So if you want to do something like your first example you may need to write the loop like this to get a similar effect:

for(global.i = 0; global.i < 100000000; global.i++) { }

也许有一种方法可以告诉Node不使用它的外包装函数;我对Node的建议并不熟悉。

Perhaps there is a way to tell Node to not use its outer wrapper function; I'm not familiar enough with Node to advise on that.

这篇关于好奇的JavaScript性能取决于变量范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆