2012-08-31 179 views
5

测试此代码在Flash:为什么i = i + 1比i ++快?

var i:int = 0; 
for (var j:int = 0; j < 5000000; j++) 
{ 
    i=i+1; 
}// use about 300ms. 

i = 0; 
for (var j:int = 0; j < 5000000; j++) 
{ 
    i++; 
}// use about 400ms 

i = 0; 
for (var j:int = 0; j < 5000000; j++) 
{ 
    ++i; 
}// use about 400ms too 

为什么i=i+1在ActionScript 3快时,它在别人慢半拍?

对不起,我犯了一些错误。上面的代码使用同一时间。 但如果把它放到函数中,结果会不一样。

var i:int; 
var j:int; 
var startTime:Number; 

function func1():void 
{ 
    i = i + 1; 
} 

function func2():void 
{ 
    i++; 
} 

startTime = getTimer(); 
i = 0; 
for (j = 0; j < 10000000; j++) 
{ 
    func1(); 
} 
trace(getTimer() - startTime);//5 times:631,628,641,628,632 

startTime = getTimer(); 
i = 0; 
for (j = 0; j < 10000000; j++) 
{ 
    func2(); 
} 
trace(getTimer() - startTime);//5 times:800,814,791,832,777 
+3

您可以10倍环路长度,并确保真的有区别吗? – scientiaesthete

+0

你是什么意思“其他人比较慢?”其他编程语言?哪个? –

+0

一个小方面说明:如果你有一个release-/debugbuild(在FlashIDE中的'允许调试'设置),那么这很重要,并且结果在发布版和调试版中也会有所不同。 http://jacksondunstan.com对多个玩家进行了很多非常深入的性能测试。 –

回答

5

如果你的循环位于会对性能产生很大的影响。如果你的循环在函数内部,Flash将使用本地寄存器执行计算。因此含有i++环路产生以下的操作码:

000013 inclocal_i (REG_2) ; increment i 
000015 inclocal_i (REG_3) ; increment j 
000017 getlocal (REG_3) ; push j onto stack 
000018 pushint 5000000  ; push 5000000 onto stack 
000020 iflt -12   ; jump backward if less than 

含有i = i + 1环路产生以下:

000013 getlocal (REG_2) ; push i onto stack 
000014 pushbyte 1   ; push 1 onto stack 
000016 add     ; add the two 
000017 convert_i   ; coerce to integer 
000018 setlocal (REG_2) ; save i back to register 2 
000019 inclocal_i (REG_3) 
000021 getlocal (REG_3) 
000022 pushint 5000000 
000024 iflt -16 

i++i = i + 1这里更快,因为inclocal_i直接修改寄存器,而无需加载注册到堆栈并保存回去。

当您将其放入框架脚本中时,循环变得效率低下。 Flash会将声明的变量存储为类变量。访问这些需要更多的工作。该i++循环结果如下:

000017 getlocal (REG_0, this) ; push this onto stack 
000018 dup      ; duplicate it 
000019 setlocal (REG_2)   ; save this to register 2 
000020 getproperty i   ; get property "i" 
000022 increment_i    ; add one to it 
000023 setlocal (REG_3)   ; save result to register 3 
000024 getlocal (REG_2)   ; get this from register 2 
000025 getlocal (REG_3)   ; get value from register 3 
000026 setproperty i   ; set property "i" 
000028 kill (REG_3)    ; kill register 2 
000030 kill (REG_2)    ; kill register 3 
000032 getlocal (REG_0, this) ; do the same thing with j... 
000033 dup 
000034 setlocal (REG_2) 
000035 getproperty j 
000037 increment_i 
000038 setlocal (REG_3) 
000039 getlocal (REG_2) 
000040 getlocal (REG_3) 
000041 setproperty j 
000043 kill (REG_3) 
000045 kill (REG_2) 
000047 getlocal (REG_0, this) 
000048 getproperty j 
000050 pushint 5000000 
000052 iflt -40 

i = i + 1版本是有点短:

000017 getlocal (REG_0, this) ; push this onto stack 
000018 getlocal (REG_0, this) ; push this onto stack 
000019 getproperty i   ; get property "i" 
000021 pushbyte 1    ; push 1 onto stack 
000023 add      ; add the two 
000024 initproperty i   ; save result to property "i" 
000026 getlocal (REG_0, this) ; increment j... 
000027 dup 
000028 setlocal (REG_2) 
000029 getproperty j 
000031 increment_i 
000032 setlocal (REG_3) 
000033 getlocal (REG_2) 
000034 getlocal (REG_3) 
000035 setproperty j 
000037 kill (REG_3) 
000039 kill (REG_2) 
000041 getlocal (REG_0, this) 
000042 getproperty j 
000044 pushint 5000000 
000046 iflt -34 
+0

我喜欢你的答案比我的好多了:)你怎么得到操作码,你只是修改mm.cfg莫名其妙? – shaunhusain

+1

我写了一个AS3拆装器。你可以在http://flaczki.net46.net/codedump/查看。性能优化可能会很难。 – cleong

+0

谢谢你真棒!我正在过渡到HTML5/JS,所以我不得不依靠AS3的东西一点,但我一定会将其归档,以便尽快结账。 – shaunhusain

4

我不能复制这种行为这三个似乎是差不多的时候,我

Attempt 1 
loop 1: 378 
loop 2: 396 
loop 3: 382 

Attempt 2 
loop 1: 380 
loop 2: 361 
loop 3: 361 

Attempt 3 
loop 1: 375 
loop 2: 373 
loop 3: 355 

由10倍增加循环我这些时间:

Attempt 1 
loop 1: 3707 
loop 2: 3663 
loop 3: 3653 

Attempt 2 
loop 1: 3722 
loop 2: 3632 
loop 3: 3711 

[TestLoopSpeed.as]

package 
{ 
    import flash.display.Sprite; 
    import flash.utils.getTimer; 

    public class TestLoopSpeed extends Sprite 
    { 
     public function TestLoopSpeed() 
     { 
      var timeNow:Number = getTimer(); 
      var i:int = 0; 

      var startOne:Number = getTimer(); 
      for (var j:int = 0; j < 5000000; j++) 
      { 
       i=i+1; 
      } 
      var endOne:Number = getTimer(); 


      var startTwo:Number = getTimer(); 
      i = 0; 
      for (var j:int = 0; j < 5000000; j++) 
      { 
       i++; 
      } 
      var endTwo:Number = getTimer(); 

      var startThree:Number = getTimer(); 
      i = 0; 
      for (var j:int = 0; j < 5000000; j++) 
      { 
       ++i; 
      } 
      var endThree:Number = getTimer(); 

      trace("loop 1: " + (endOne - startOne)); 
      trace("loop 2: " + (endTwo - startTwo)); 
      trace("loop 3: " + (endThree - startThree)); 
     } 
    } 
} 

据我了解,i ++最终等价于i = i + 1;不同之处在于,如果在该行上正在进行分配,那么将使用i的当前值,稍后的指令将为i增加一个值。 ++我简单的说就是在对这行进行任何其他操作之前给我加1。最终,我不认为这些选项中的任何一个都会真正影响从我做过的每个测试中看出的性能,因为在任何给定时刻,对于闪存进程而言,CPU调度比任何给定的运算符都具有更多的效果。

如果我用来测试的代码有问题,请指出。

编辑

更新的代码,包括while循环选项,仍然没有看到任何看起来像一个积极的显著差异:

loop 1: 3695 
loop 2: 3698 
loop 3: 3690 
loop 4: 3711 

loop 1: 3758 
loop 2: 3651 
loop 3: 3707 
loop 4: 3676 

[TestLoopSpeed.as] 更新花式而循环预增量动作

package 
{ 
    import flash.display.Sprite; 
    import flash.utils.getTimer; 

    public class TestLoopSpeed extends Sprite 
    { 
     public function TestLoopSpeed() 
     { 
      var timeNow:Number = getTimer(); 
      var i:int = 0; 

      var startOne:Number = getTimer(); 
      for (var j:int = 0; j < 50000000; j++) 
      { 
       i=i+1; 
      } 
      var endOne:Number = getTimer(); 


      var startTwo:Number = getTimer(); 
      i = 0; 
      for (var j:int = 0; j < 50000000; j++) 
      { 
       i++; 
      } 
      var endTwo:Number = getTimer(); 

      var startThree:Number = getTimer(); 
      i = 0; 
      for (var j:int = 0; j < 50000000; j++) 
      { 
       ++i; 
      } 
      var endThree:Number = getTimer(); 

      var startFour:Number = getTimer(); 
      i = 0; 
      var j:int = -1; 
      while (++j < 50000000) 
      { 
       ++i; 
      } 
      var endFour:Number = getTimer(); 

      trace("loop 1: " + (endOne - startOne)); 
      trace("loop 2: " + (endTwo - startTwo)); 
      trace("loop 3: " + (endThree - startThree)); 
      trace("loop 4: " + (endFour - startFour)); 
     } 
    } 
} 
0

我没有对你的问题的回应,但以下是我尝试过的所有循环中最快的。

import flash.utils.getTimer; 

var t:int = getTimer(); 
var i:int = 0, j:int = -1; 
while (++j < 5000000) { 
    i += 1; 
} 
trace(getTimer()-t) 

这给了我83毫秒。

2

++和 - 操作符被设计成类似汇编代码的增量和减量,但是现在它不应该与编译器的进步有很大的区别。 See section increase and decrease

它可能是实施中的更改,或者是actionscript虚拟机中的临时regression

0

我建议不要使用i++;使用++i;因为它比你提到的要快。

看这很好的解释:What is more efficient i++ or ++i?

+0

值得一提的是,在每个操作之后,变量不会保持相同的值,这意味着如果不更改使用此变量的代码,它们就不可互换。 –

0

还取决于所有的语言。 Maby在AS3 $ i = $ i + 1中速度更快,在PHP $ i ++中速度更快。但像Almas Adilbek所说的那样。 ++ $ i是最快的。

<?php 
    $start = microtime(); 

    $i = 0; 
    while($i != 100000) 
    { 
     $i++; 
    } 

    $end = microtime(); 

    echo 'First complete in: ' . ($end - $start); 

    $start = microtime(); 

    $i = 0; 
    while($i != 100000) 
    { 
     $i = $i+1; 
    } 

    $end = microtime(); 

    echo 'Second complete in: ' . ($end - $start); 

    $start = microtime(); 

    $i = 0; 
    while($i != 100000) 
    { 
     ++$i; 
    } 

    $end = microtime(); 

    echo 'Third complete in: ' . ($end - $start); 
?> 
相关问题