奇怪的 JavaScript 性能取决于变量范围
Curious JavaScript performance dependent on variable scope
在测试一个 JavaScript 项目的性能时,我注意到一个非常奇特的行为 - JavaScript 成员访问性能似乎受到它们所在范围的严重影响。我写了一些性能测试,结果相差好几个数量级。
我在 Windows 10 64 位上测试了以下浏览器:
- 谷歌浏览器,版本49.0.2623.75 m - 使用V8 JavaScript引擎
- Mozilla Firefox 版本 44.0.2 - 使用 SpiderMonkey JavaScript 引擎
- Microsoft Edge,版本25.10586 - 使用Chakra JavaScript引擎
以下是我运行的最相关的测试及其各自的结果:
// 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%的性能提升。
最大的惊喜是在本地范围内访问变量,在Chrome和Firefox上快1200到1600倍,在Edge上快90倍。
为什么在三个不同的浏览器/JavaScript引擎上会这样?
通过在 .js Node 下运行代码并在node
命令行上传递 --print_opt_code
开关,您可以查看 V8 JavaScript 引擎生成的实际机器代码(与 Chrome 中使用的相同)。例如,如果将代码放在名为 test.js
的文件中,则可以运行:
node --print_opt_code test.js
在上一个示例中,V8 能够将 i
变量放入RAX
寄存器中,而不是将其保存在内存中。这是上述命令打印的代码的内部循环,带有一些额外的注释。(之前和之后都有额外的代码;这只是内部循环本身。
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
以十六进制表示。
如您所见,这是一个相当紧密的循环,只有几条指令。大多数代码是 JavaScript 代码的直接翻译;我唯一有点不确定的是地址 97 和 104 上的两条指令,如果满足某个条件,它们就会退出循环。
如果你用其他版本的JavaScript代码运行类似的测试,你会看到更长的指令序列。请注意,Node 将所有代码包装在它提供的包装函数中。因此,如果你想做一些类似于你的第一个示例的事情,你可能需要像这样编写循环以获得类似的效果:
for(global.i = 0; global.i < 100000000; global.i++) { }
也许有一种方法可以告诉 Node 不要使用其外部包装器函数;我对 Node 不够熟悉,无法就此提供建议。
全局命名空间中的变量的性能要差得多,但不完全是因为@Freddie提到的原因。全局命名空间中的变量可能会被外部内容更改,从而强制解释器每次通过循环重新加载值。使用局部变量,JIT 引擎可以将循环优化为每次迭代几个机器周期,这似乎是这里正在发生的事情。
在技术 1 - http://www.webreference.com/programming/javascript/jkm3/index.html 下查看此内容
全局变量的性能很慢,因为它们位于高度填充的命名空间中。它们不仅与许多其他用户定义的量和 JavaScript 变量一起存储,浏览器还必须区分全局变量和当前上下文中对象的属性。当前上下文中的许多对象都可以由变量名称引用,而不是作为对象属性引用,例如 alert() 与 window.alert() 同义。缺点是这种便利性会减慢使用全局变量的代码。
- 将函数的上下文应用于javascript变量
- 无法导出函数expressjs/requestjs中的变量
- 函数参数中的数据与指定变量之间的任何性能差异
- 将PHP变量传递给jQuery时遇到问题
- 如何通过ajax刷新JSF填充的javascript变量
- 参数变量出现ngTable指令问题
- 通过javascript重定向html传递php变量
- 将jsp文件下拉列表中的选定项分配给一个java变量(比如String selection)
- 全局变量和全局对象的属性之间有什么区别吗
- 如何创建数组取决于是否在jQuery中设置了变量
- 脚本中的变量取决于用户输入的文本字段
- 奇怪的 JavaScript 性能取决于变量范围
- 我将如何编写考虑多个单选按钮和变量输出的 JavaScript,具体取决于选择的单选按钮
- 原型取决于全局变量(如果该变量发生变化怎么办)
- 如何在网站中为JS变量赋值,取决于它是否's正在本地运行或正在生产中
- JavaScript对象的初始化时间取决于变量和函数的数量
- 我想要多个变量切换为真或假取决于当前的路由
- 使背景半彩色取决于变量
- 如何显示的东西取决于变量给定的角
- 如何跨多个URL使用相同的HTML页面,除了一个变量的值取决于所查询的URL