后增量与预增量 - Javascript优化

mau*_*ris 34 javascript optimization increment

当我偶然发现这个名为JSpeed的项目 - Javascript优化时,我正在浏览Google Code.

我注意到优化的一个是改变i++++i在for循环语句.

优化之前

for (i=0;i<1;i++) {}

for (var i = 0, j = 0; i < 1000000; i++, j++) {
    if (i == 4) {
        var tmp = i / 2;
    }

    if ((i % 2) == 0) {
        var tmp = i / 2;
        i++;
    }
}
var arr = new Array(1000000);
for (i = 0; i < arr.length; i++) {}
Run Code Online (Sandbox Code Playgroud)

优化后

for(var i=0;i<1;++i){}
for(var i=0,j=0;i<1000000;++i,++j){if(i==4){var tmp=i>>1;}
if((i&1)==0){var tmp=i>>1;i++;}}
var arr=new Array(1000000);for(var i=0,arr_len=arr.length;i<arr_len;++i){}
Run Code Online (Sandbox Code Playgroud)

我知道前后增量有什么作用,但是任何想法如何加速代码?

Jus*_*son 58

这是一种虚假的优化.据我了解,你节省了1个操作码.如果您希望使用这种技术优化代码,那么您就走错了路.此外,大多数编译器/解释器无论如何都会为您优化(参考文献1).总之,我不担心. 但是,如果你真的很担心,你应该使用i+=1.

这是我刚才做的快速而肮脏的基准测试

var MAX = 1000000, t=0,i=0;

t = (new Date()).getTime();
for ( i=0; i<MAX;i++ ) {}
t = (new Date()).getTime() - t;

console.log(t);

t = (new Date()).getTime();
for ( i=0; i<MAX;++i ) {}
t = (new Date()).getTime() - t;

console.log(t);

t = (new Date()).getTime();
for ( i=0; i<MAX;i+=1 ) {}
t = (new Date()).getTime() - t;

console.log(t);
Run Code Online (Sandbox Code Playgroud)

原始结果

Post    Pre     +=
1071    1073    1060
1065    1048    1051
1070    1065    1060
1090    1070    1060
1070    1063    1068
1066    1060    1064
1053    1063    1054
Run Code Online (Sandbox Code Playgroud)

删除最低和最高

Post    Pre     +=
1071    ----    1060
1065    ----    ----
1070    1065    1060
----    1070    1060
1070    1063    ----
1066    1060    1064
----    1063    1054
Run Code Online (Sandbox Code Playgroud)

平均值

1068.4  1064.2  1059.6
Run Code Online (Sandbox Code Playgroud)

请注意,这超过一百万次迭代,结果平均在9毫秒内.考虑到JavaScript中的大多数迭代处理是通过更小的集合(例如DOM容器)完成的,并不是真正的优化.

  • 我的观点是差异可以忽略不计,并且在较小的数据集(<1000)中无法真正区分,这在JavaScript中比在更大的数据集中更常见.通常,在JavaScript中迭代的数据集是DOM集合,通常不超过200个成员.即便如此,在这些情况下,瓶颈是DOM,而不是pre vs post vs + =的最小优化 (9认同)

Koo*_*Inc 51

这是我读到的并且可以回答你的问题:"preincrement(++i)在值中加一i,然后返回i;相反,i++返回i然后向它添加一个,这在理论上导致创建一个存储值的临时变量i在应用增量操作之前".

  • 看到这个性能测试:http://jsperf.com/preincrement-vs-postincrement-vs-predecrement-vs-postde/5 (2认同)
  • i = 1; i = i ++; 的console.log(ⅰ); // 1 i = 1; 我= ++ i; 的console.log(ⅰ); // 2 (2认同)

Syl*_*oux 7

理论上,使用后增量运算符可能会产生临时值.在实践中,JavaScript编译器足够聪明以避免这种情况,特别是在这种微不足道的情况下.

例如,让我们考虑一下示例代码:

sh$ cat test.js 
function preInc(){
  for(i=0; i < 10; ++i)
    console.log(i);
}

function postInc(){
  for(i=0; i < 10; i++)
    console.log(i);
}

// force lazy compilation
preInc();
postInc();
Run Code Online (Sandbox Code Playgroud)

在这种情况下,NodeJS中的V8编译器产生完全相同的字节码(特别是在操作码39-44处增加):

sh$ node --version
v8.9.4
sh$ node --print-bytecode test.js | sed -nEe '/(pre|post)Inc/,/^\[/p'
[generating bytecode for function: preInc]
Parameter count 1
Frame size 24
   77 E> 0x1d4ea44cdad6 @    0 : 91                StackCheck 
   87 S> 0x1d4ea44cdad7 @    1 : 02                LdaZero 
   88 E> 0x1d4ea44cdad8 @    2 : 0c 00 03          StaGlobalSloppy [0], [3]
   94 S> 0x1d4ea44cdadb @    5 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44cdade @    8 : 1e fa             Star r0
         0x1d4ea44cdae0 @   10 : 03 0a             LdaSmi [10]
   94 E> 0x1d4ea44cdae2 @   12 : 5b fa 07          TestLessThan r0, [7]
         0x1d4ea44cdae5 @   15 : 86 23             JumpIfFalse [35] (0x1d4ea44cdb08 @ 50)
   83 E> 0x1d4ea44cdae7 @   17 : 91                StackCheck 
  109 S> 0x1d4ea44cdae8 @   18 : 0a 01 0d          LdaGlobal [1], [13]
         0x1d4ea44cdaeb @   21 : 1e f9             Star r1
  117 E> 0x1d4ea44cdaed @   23 : 20 f9 02 0f       LdaNamedProperty r1, [2], [15]
         0x1d4ea44cdaf1 @   27 : 1e fa             Star r0
  121 E> 0x1d4ea44cdaf3 @   29 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44cdaf6 @   32 : 1e f8             Star r2
  117 E> 0x1d4ea44cdaf8 @   34 : 4c fa f9 f8 0b    CallProperty1 r0, r1, r2, [11]
  102 S> 0x1d4ea44cdafd @   39 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44cdb00 @   42 : 41 0a             Inc [10]
  102 E> 0x1d4ea44cdb02 @   44 : 0c 00 08          StaGlobalSloppy [0], [8]
         0x1d4ea44cdb05 @   47 : 77 2a 00          JumpLoop [42], [0] (0x1d4ea44cdadb @ 5)
         0x1d4ea44cdb08 @   50 : 04                LdaUndefined 
  125 S> 0x1d4ea44cdb09 @   51 : 95                Return 
Constant pool (size = 3)
Handler Table (size = 16)
[generating bytecode for function: get]
[generating bytecode for function: postInc]
Parameter count 1
Frame size 24
  144 E> 0x1d4ea44d821e @    0 : 91                StackCheck 
  154 S> 0x1d4ea44d821f @    1 : 02                LdaZero 
  155 E> 0x1d4ea44d8220 @    2 : 0c 00 03          StaGlobalSloppy [0], [3]
  161 S> 0x1d4ea44d8223 @    5 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44d8226 @    8 : 1e fa             Star r0
         0x1d4ea44d8228 @   10 : 03 0a             LdaSmi [10]
  161 E> 0x1d4ea44d822a @   12 : 5b fa 07          TestLessThan r0, [7]
         0x1d4ea44d822d @   15 : 86 23             JumpIfFalse [35] (0x1d4ea44d8250 @ 50)
  150 E> 0x1d4ea44d822f @   17 : 91                StackCheck 
  176 S> 0x1d4ea44d8230 @   18 : 0a 01 0d          LdaGlobal [1], [13]
         0x1d4ea44d8233 @   21 : 1e f9             Star r1
  184 E> 0x1d4ea44d8235 @   23 : 20 f9 02 0f       LdaNamedProperty r1, [2], [15]
         0x1d4ea44d8239 @   27 : 1e fa             Star r0
  188 E> 0x1d4ea44d823b @   29 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44d823e @   32 : 1e f8             Star r2
  184 E> 0x1d4ea44d8240 @   34 : 4c fa f9 f8 0b    CallProperty1 r0, r1, r2, [11]
  168 S> 0x1d4ea44d8245 @   39 : 0a 00 05          LdaGlobal [0], [5]
         0x1d4ea44d8248 @   42 : 41 0a             Inc [10]
  168 E> 0x1d4ea44d824a @   44 : 0c 00 08          StaGlobalSloppy [0], [8]
         0x1d4ea44d824d @   47 : 77 2a 00          JumpLoop [42], [0] (0x1d4ea44d8223 @ 5)
         0x1d4ea44d8250 @   50 : 04                LdaUndefined 
  192 S> 0x1d4ea44d8251 @   51 : 95                Return 
Constant pool (size = 3)
Handler Table (size = 16)
Run Code Online (Sandbox Code Playgroud)

当然,其他JavaScript编译器/解释器可能会这样做,但这是值得怀疑的.

最后,尽管它值得,但我仍然认为在可能的情况下使用预增量是最好的做法:因为我经常切换语言,我更喜欢使用具有正确语义的语法来实现我想要的,而不是依赖于编译器机灵.例如,现代C编译器也没有任何区别.但是在C++中,这会对重载产生重大影响operator++.