2012-08-31 12 views
5

Verifica questo codice in Flash:Perché i = i + 1 è più veloce di 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 

Perché i=i+1 più veloce in ActionScript 3, quando è più lento in altri?

Spiacente, faccio qualche errore. Il codice sopra riportato usa la stessa ora. ma se lo metti in funzione, il risultato sarà diverso.

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

Riesci a volte 10 la lunghezza del loop, e assicurarsi che ci sia davvero una differenza? – scientiaesthete

+0

Cosa intendi con "più lento negli altri?" Altri linguaggi di programmazione? Quale? –

+0

Una piccola nota a margine: è importante se si dispone di un'impostazione release-/debugbuild ('permesso il debugging' in FlashIDE), e i risultati potrebbero anche variare in un release e debugplayer. http://jacksondunstan.com ha molti test di prestazione molto approfonditi per diversi giocatori. –

risposta

5

Dove il ciclo si trova può avere un grande impatto sulle prestazioni. Se il loop si trova all'interno di una funzione, Flash eseguirà calcoli utilizzando i registri locali. L'anello contenente i++ produce così i seguenti codici operativi:

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 

loop contenente i = i + 1 produce i seguenti:

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++ è più veloce di i = i + 1 qui dato inclocal_i modifica il registro direttamente, senza dover caricare la registrarsi sullo stack e salvarlo nuovamente.

Il ciclo diventa molto meno efficiente quando lo si inserisce in uno script di frame. Flash memorizzerà le variabili dichiarate come variabili di classe. L'accesso a quelli richiede più lavoro. I risultati i++ ciclo nel seguente:

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 

La versione i = i + 1 è po 'più breve:

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

Mi piace la tua risposta molto meglio della mia :) come ottieni gli opcode hai appena modificato mm.cfg in qualche modo? – shaunhusain

+1

Ho scritto un disassemblatore AS3. Puoi verificarlo su: http://flaczki.net46.net/codedump/. Può essere forte per l'ottimizzazione delle prestazioni. – cleong

+0

Grazie, è fantastico! Sto passando ad HTML5/JS, quindi devo riposare un po 'sull'AS3 per un po' ma lo archivierò sicuramente per il check-out a breve. – shaunhusain

4

non posso replicare questo comportamento tutti e tre sembrano essere circa lo stesso tempo per me

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 

Aumentando le anse di un fattore 10 ho ottenuto questi tempi:

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

Per quanto ho capito, i ++ è in definitiva equivalente a i = i + 1; con l'eccezione che se un assegnamento si sta verificando su quella linea, allora il valore corrente di i verrebbe utilizzato e un'istruzione successiva aggiungerebbe uno a i. ++ Voglio dire semplicemente aggiungere 1 a I prima di fare qualsiasi altra operazione su questa linea. In ultima analisi, non credo che nessuna di queste opzioni debba davvero influire sulle prestazioni che appare da ogni test che ho fatto che la schedulazione della CPU in un dato momento per il processo flash abbia più effetto di un dato operatore.

Se c'è qualcosa che non va nel codice che sto usando per testarlo, per favore segnalalo.

Modifica

Aggiornato il codice per includere l'opzione ciclo while, ancora non vedere tutto ciò che si presenta come una significativa differenza positiva:

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] AGGIORNATO CON mentre fantasia ciclo pre-incremento azione

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

Non ho una risposta alla tua domanda, ma il seguente è stato il più veloce tra tutti i loop che ho provato.

import flash.utils.getTimer; 

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

Questo mi dà 83 ms.

2

++ e - gli operatori sono stati progettati per assomigliare al codice assembly per incremento e decremento, ma al giorno d'oggi non dovrebbe fare molta differenza con l'avanzamento dei compilatori. See section increase and decrease

Potrebbe essere un cambiamento nell'implementazione o uno regression temporaneo nella macchina virtuale ActionScript.

0

Suggerisco invece di i++; utilizzare ++i; Perché è più veloce di quanto non hai menzionato.

Guardate questa buona spiegazione: What is more efficient i++ or ++i?

+0

Vale la pena ricordare che una variabile non manterrà lo stesso valore dopo ognuna di quelle operazioni, il che significa che non sono intercambiabili senza alterare il codice che utilizza questa variabile. –

0

dipende anche tutto sulla lingua. Maby in AS3 $ i = $ i + 1 è più veloce, in PHP $ i ++ lo è. Ma come racconta Almas Adilbek. Il ++ $ i è il più veloce.

Esempio

<?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); 
?> 
Problemi correlati