【问题标题】:Variable name length vs performance变量名称长度与性能
【发布时间】:2015-07-30 09:22:52
【问题描述】:

变量名长度的巨大差异怎么可能不会导致javascript中的任何性能损失?

声明var a = 0; 的时间与声明var aaaaaaaaaaaaaaa = 0; 的时间相同 甚至用它们执行计算也需要同样的时间。

My fiddle to demonstrate

【问题讨论】:

  • 这可能有already been answered
  • 因为两个变量在内存中的存储方式相同。他们获得一个内存地址,在执行代码时使用该地址。 可能在变量的声明中存在细微差别。 如果有,可以忽略不计。
  • @Cerbrus,有一个可信的参考,以及一些关于内存寻址机制的小细节,我也可以接受你的解释作为答案。

标签: javascript performance variables naming-conventions


【解决方案1】:
window.a = 2;
window.b = 3;
window.c = 4;
window.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 2;
window.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = 3;
window.ccccccccccccccccccccccccccccccccccccccccccccccccc = 4;    
var ts = [];

var t = performance.now();
for(var i = 0; i < 1000000; ++i) {
    a = b + c;
}
ts.push(performance.now() - t);

var t = performance.now();
for(var i = 0; i < 1000000; ++i) {
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+ ccccccccccccccccccccccccccccccccccccccccccccccccc;
}
ts.push(performance.now() - t);

console.log(ts);

在浏览器控制台中运行上述 10 次会得到以下统计信息,即 a=b+c、a=b...+c... 对:

[4.050000000046566, 4.614999999990687]
[4.254999999946449, 4.59499999997206]
[4.054999999993015, 4.584999999962747]
[4.869999999995343, 5.4500000000116415]
[4.074999999953434, 4.570000000006985]
[4.099999999976717, 4.775000000023283]
[4.205000000016298, 4.649999999965075]
[4.205000000016298, 4.669999999983702]
[4.159999999974389, 4.720000000030268]
[4.149999999965075, 4.684999999997672]

较长的版本总是较慢。

在局部变量的情况下,这是不同的,因为它们被编译一次并使用 getlocal/setlocal 指令通过索引而不是名称来引用。那么让我们看看..

(function() {
    var a = 2;
    var b = 3;
    var c = 4;
    var aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 2;
    var bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = 3;
    var ccccccccccccccccccccccccccccccccccccccccccccccccc = 4;    
    var ts = [];

    var t = performance.now();
    for(var i = 0; i < 1000000; ++i) {
        a = b + c;
    }
    ts.push(performance.now() - t);

    var t = performance.now();
    for(var i = 0; i < 1000000; ++i) {
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+ ccccccccccccccccccccccccccccccccccccccccccccccccc;
    }
    ts.push(performance.now() - t);

    console.log(ts);
})();

[2.5850000000791624, 2.2100000000791624] (longer wins)
[2.7950000000419095, 2.525000000023283] (shorter wins)
[2.4699999999720603, 2.4200000000419095] (longer wins)
[2.64000000001397, 2.2449999999953434] (longer wins)
[2.669999999925494, 2.469999999855645] (longer wins)
[2.5200000000186265, 2.7800000000279397] (shorter wins)
[2.4600000000791624, 2.3950000000186265] (longer wins)
[3.2900000001536682, 3.1299999998882413] (longer wins)
[3.1949999999487773, 3.1850000000558794] (longer wins)
[3.8049999999348074, 3.0200000000186265] (longer wins)

虽然它们在大多数迭代中确实变化得相当惊人,但在很多情况下,较长的变量名比较短的变量名更快(一个天真的观察者可能会得出结论,较长的变量名会使它更快)。这是因为 name 仅在编译范围时才相关;实际执行的指令不按名称引用变量。

我的结论是;保持全局变量名称简短,局部变量名称会稍微增加从文本到指令的翻译,但在此之后,就没有关系了。

我假设 javascript 在处理变量方面的工作方式与 actionscript 类似,因此这里是两个字节码并排的 actionscript 转储。 (Adobe Flash CS3;使用 JPEXS 免费 Flash 反编译器进行反编译)。

var a;
var b = 2;
var c = 3;
var aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
var bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2;
var ccccccccccccccccccccccccccccccccccccccccccccccccc = 3;    

function long_global()
{        
    for(var i = 0; i < 1000000; ++i) {
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccccccccccccccccccccccccccc;
    }
}

function short_global()
{
    for(var i = 0; i < 1000000; ++i) {
        a = b + c;
    }
}

function long_local()
{        
    var aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
    var bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2;
    var ccccccccccccccccccccccccccccccccccccccccccccccccc = 3;

    for(var i = 0; i < 1000000; ++i) {
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccccccccccccccccccccccccccc;
    }
}

function short_local()
{
    var a;
    var b = 2;
    var c = 3;

    for(var i = 0; i < 1000000; ++i) {
        a = b + c;
    }
}

long_global 编译为:

; d0
getlocal_0
; 30
pushscope
; 21
pushundefined
; 82
coerce_a
; d5
setlocal_1
; 24 00
pushbyte 0
; 82
coerce_a
; d5
setlocal_1
; 10 0e 00 00
jump ofs001b
; 09
ofs000d:label
; 5e 0a
findproperty Qname(PackageNamespace(""),"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
; 60 09
getlex Qname(PackageNamespace(""),"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
; 60 06
getlex Qname(PackageNamespace(""),"ccccccccccccccccccccccccccccccccccccccccccccccccc")
; a0
add
; 68 0a
initproperty Qname(PackageNamespace(""),"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
; d1
getlocal_1
; 91
increment
; 82
coerce_a
; d5
setlocal_1
; d1
ofs001b:getlocal_1
; 2d 01
pushint 1000000
; 15 eb ff ff
iflt ofs000d
; 47
returnvoid

short_global 编译为

; d0
getlocal_0
; 30
pushscope
; 21
pushundefined
; 82
coerce_a
; d5
setlocal_1
; 24 00
pushbyte 0
; 82
coerce_a
; d5
setlocal_1
; 10 0e 00 00
jump ofs001b
; 09
ofs000d:label
; 5e 08
findproperty Qname(PackageNamespace(""),"a")
; 60 05
getlex Qname(PackageNamespace(""),"b")
; 60 04
getlex Qname(PackageNamespace(""),"c")
; a0
add
; 68 08
initproperty Qname(PackageNamespace(""),"a")
; d1
getlocal_1
; 91
increment
; 82
coerce_a
; d5
setlocal_1
; d1
ofs001b:getlocal_1
; 2d 01
pushint 1000000
; 15 eb ff ff
iflt ofs000d
; 47
returnvoid

long_local:

; d0
getlocal_0
; 30
pushscope
; 21
pushundefined
; 82
coerce_a
; d5
setlocal_1
; 21
pushundefined
; 82
coerce_a
; d6
setlocal_2
; 21
pushundefined
; 82
coerce_a
; d7
setlocal_3
; 21
pushundefined
; 82
coerce_a
; 63 04
setlocal 4
; 24 02
pushbyte 2
; 82
coerce_a
; d6
setlocal_2
; 24 03
pushbyte 3
; 82
coerce_a
; d7
setlocal_3
; 24 00
pushbyte 0
; 82
coerce_a
; 63 04
setlocal 4
; 10 0c 00 00
jump ofs002c
; 09
ofs0020:label
; d2
getlocal_2
; d3
getlocal_3
; a0
add
; 82
coerce_a
; d5
setlocal_1
; 62 04
getlocal 4
; 91
increment
; 82
coerce_a
; 63 04
setlocal 4
; 62 04
ofs002c:getlocal 4
; 2d 01
pushint 1000000
; 15 ec ff ff
iflt ofs0020
; 47
returnvoid

short_local:

; d0
getlocal_0
; 30
pushscope
; 21
pushundefined
; 82
coerce_a
; d5
setlocal_1
; 21
pushundefined
; 82
coerce_a
; d6
setlocal_2
; 21
pushundefined
; 82
coerce_a
; d7
setlocal_3
; 21
pushundefined
; 82
coerce_a
; 63 04
setlocal 4
; 24 02
pushbyte 2
; 82
coerce_a
; d6
setlocal_2
; 24 03
pushbyte 3
; 82
coerce_a
; d7
setlocal_3
; 24 00
pushbyte 0
; 82
coerce_a
; 63 04
setlocal 4
; 10 0c 00 00
jump ofs002c
; 09
ofs0020:label
; d2
getlocal_2
; d3
getlocal_3
; a0
add
; 82
coerce_a
; d5
setlocal_1
; 62 04
getlocal 4
; 91
increment
; 82
coerce_a
; 63 04
setlocal 4
; 62 04
ofs002c:getlocal 4
; 2d 01
pushint 1000000
; 15 ec ff ff
iflt ofs0020
; 47
returnvoid

long_global 和 short_global 的字节码大小;和 short_local 和 long_local 分别是等价的,但是在本地情况下,它们不被名称引用,仅被 setlocal 和 getlocal 引用;而在全局情况下,它们由字符串键所在位置的索引引用;我的猜测是,越长的哈希获取所指对象所需的时间越长,而较短的哈希获取所指对象所需的时间越短。

【讨论】:

  • 请注意,使全局变量名称变短违背了 Linux 内核编码风格(这只是指导方针),请实际上不要将全局变量命名为 a、b、c,除非它们在您的代码。我说的是 jquery 的 $(在任何机会都可以使用 jquery 的代码中)或 Math.PI(如果 javascript 编译器识别出 Math.PI 并内联它,它在循环中仍然可能更快)或 eax/rax 寄存器级别这里很热。
  • 感谢@Dmitry 的精彩文章,对全局变量和局部变量命名之间的解释非常有帮助
【解决方案2】:

基于@Cerbrus 的评论:

它在实践中导致可以忽略不计小的性能差异,但仅在声明变量时。 之后得到一个具体的address in the memory,引用这个地址,而不是变量名,不再影响性能。


然而,谈到一种主要在网络上使用的脚本语言,较短的变量名可以减小文件大小并加快页面加载速度。这是JS编译器的特性之一,比如Google's

【讨论】:

  • 并非总是如此;这仅适用于局部变量,例如显式参数和范围变量。当您执行 window.bar = 2 之类的操作时,它不会使用 getlocal_index 来引用,它必须获取字符串 bar 并通过散列名称来查找实际值。所以 window.gkijqjqiwiwiwododo = window.qwdoijqwodjqoiwjdiqjwdiwq + window.cjqjiwjqijwijwijwiwiwk 在声明之后会比 window.a = window.b + window.c 慢得可以忽略不计。有点想现在测试它。
  • 这是否也适用于对象属性名称?
  • 问得好,@WinstonJude!我去看看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-13
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多