代码之家  ›  专栏  ›  技术社区  ›  mauris

后增量vs前增量-javascript优化

  •  32
  • mauris  · 技术社区  · 15 年前

    我在浏览谷歌代码的时候,偶然发现了一个名为jspeed的项目——JavaScript优化。

    我注意到其中一个优化是改变 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++) {}
    

    优化后

    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){}
    

    我知道什么是前增量和后增量,但知道这是如何加速代码的吗?

    8 回复  |  直到 7 年前
        1
  •  47
  •   mauris    13 年前

    这就是我读到的,可以回答你的问题:“预增量( ++i )将一个添加到 i 然后返回 相反, i++ 收益率 然后再加上一个, 理论上 结果创建一个临时变量,存储 在应用增量操作之前”。

        2
  •  55
  •   Justin Johnson    10 年前

    这是一个人造优化。据我所知,您正在保存1个操作码。如果你想用这种技术来优化你的代码,那么你就走错了。而且,大多数编译器/解释器无论如何都会为您优化这一点。( reference 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);
    

    原始结果

    Post    Pre     +=
    1071    1073    1060
    1065    1048    1051
    1070    1065    1060
    1090    1070    1060
    1070    1063    1068
    1066    1060    1064
    1053    1063    1054
    

    删除最低和最高

    Post    Pre     +=
    1071    ----    1060
    1065    ----    ----
    1070    1065    1060
    ----    1070    1060
    1070    1063    ----
    1066    1060    1064
    ----    1063    1054
    

    平均数

    1068.4  1064.2  1059.6
    

    注意这一切都结束了 一百万次迭代 结果在 平均毫秒数。考虑到JavaScript中的大多数迭代处理都是在更小的集合(例如,DOM容器)上完成的,所以并不是很好的优化。

        3
  •  6
  •   Sylvain Leroux    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();
    

    在这种情况下,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)
    

    当然,其他的javascript编译器/解释器 可以 否则,这是值得怀疑的。

    最后一句话,尽管如此,我还是认为在可能的情况下使用预增量是一种最佳实践:因为我经常切换语言,所以我更喜欢使用正确的语法。 语义的 为了我想要的,而不是依靠编译器的智能。例如,现代的C编译器也不会有任何区别。但是在C++中,这会对重载产生重大影响。 operator++ .

        4
  •  3
  •   Bill the Lizard    12 年前

    听起来像是过早的优化。当你的应用程序快完成时,检查瓶颈在哪里,并根据需要优化这些瓶颈。但是,如果您想要一个完整的循环性能指南,请查看以下内容:

    http://blogs.oracle.com/greimer/entry/best_way_to_code_a

    但是你永远不知道什么时候会因为JS引擎的改进和浏览器之间的变化而过时。最好的选择是在问题出现之前不要担心。使您的代码清晰易读。

    编辑:根据 this guy 前期与后期在统计上无显著差异。(pre可能更糟)

        5
  •  2
  •   Taylor Leese    15 年前

    优化不是前后增量。它使用的是位“shift”和“and”运算符,而不是除法和mod。

    还有缩小javascript以减小总大小的优化(但这不是运行时优化)。

        6
  •  1
  •   Mike Samuel    12 年前

    这可能是货物崇拜的程序。 当您为不存在任意运算符重载的语言使用合适的编译器/解释器时,这不会有什么不同。

    这种优化对于C++来说是有意义的。

    T x = ...;
    ++x
    

    可以就地修改值,而

    T x = ...;
    x++
    

    必须通过在引擎盖下做一些事情来创建一个副本

    T x = ...;
    T copy;
    (copy = T(x), ++x, copy)
    

    对于大型结构类型或在其'copy构造函数中进行大量计算的类型,这可能会很昂贵。

        7
  •  1
  •   pierre v    10 年前

    Anatoliy的测试包括预增量测试函数中的后增量:(

    以下是没有这种副作用的结果…

    function test_post() {
        console.time('postIncrement');
        var i = 1000000, x = 0;
        do x++; while(i--);
        console.timeEnd('postIncrement');
    }
    
    function test_pre() {
        console.time('preIncrement');
        var i = 1000000, x = 0;
        do ++x; while(--i);
        console.timeEnd('preIncrement');
    }
    
    test_post();
    test_pre();
    test_post();
    test_pre();
    test_post();
    test_pre();
    test_post();
    test_pre();
    

    产量

    postIncrement: 3.21ms
    preIncrement: 2.4ms
    postIncrement: 3.03ms
    preIncrement: 2.3ms
    postIncrement: 2.53ms
    preIncrement: 1.93ms
    postIncrement: 2.54ms
    preIncrement: 1.9ms
    

    这是一个很大的区别。

        8
  •  0
  •   Anatoliy    15 年前

    刚在Firebug中测试过,发现后增量和前增量之间没有区别。也许这个优化其他平台? 以下是我的Firebug测试代码:

    function test_post() {
        console.time('postIncrement');
        var i = 1000000, x = 0;
        do x++; while(i--);
        console.timeEnd('postIncrement');
    }
    
    function test_pre() {
        console.time('preIncrement');
        var i = 1000000, x = 0;
        do ++x; while(i--);
        console.timeEnd('preIncrement');
    }
    
    test_post();
    test_pre();
    test_post();
    test_pre();
    test_post();
    test_pre();
    test_post();
    test_pre();
    

    输出为:

    postIncrement: 140ms
    preIncrement: 160ms
    postIncrement: 136ms
    preIncrement: 157ms
    postIncrement: 148ms
    preIncrement: 137ms
    postIncrement: 136ms
    preIncrement: 148ms