Frage

Ich habe Google Code durchsucht, als ich zufällig auf dieses Projekt namens JSpeed ​​– Optimierung für Javascript – gestoßen bin.

Mir ist aufgefallen, dass eine der Optimierungen darin bestand, Änderungen vorzunehmen i++ Zu ++i in for-Schleifenanweisungen.

Vor der Optimierung

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

Nach der Optimierung

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

Ich weiß, was Pre- und Post-Inkremente bewirken, aber haben Sie eine Idee, wie dies den Code beschleunigt?

War es hilfreich?

Lösung

Folgendes habe ich gelesen und könnte Ihre Frage beantworten:„Vorinkrement (++i) addiert eins zum Wert von i, dann kehrt zurück i;im Gegensatz, i++ kehrt zurück i fügt dann eins hinzu, welches in der Theorie führt zur Erstellung einer temporären Variablen, die den Wert von speichert i bevor die Inkrementierungsoperation angewendet wurde".

Andere Tipps

Dies ist eine Scheinoptimierung.Soweit ich weiß, sparen Sie 1 Operationscode.Wenn Sie Ihren Code mit dieser Technik optimieren möchten, sind Sie auf dem falschen Weg.Außerdem optimieren die meisten Compiler/Interpreter dies ohnehin für Sie (Referenz 1).Kurz gesagt, ich würde mir darüber keine Sorgen machen. Aber, wenn Sie wirklich besorgt sind, sollten Sie verwenden i+=1.

Hier ist der Quick-and-Dirty-Benchmark, den ich gerade durchgeführt habe

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

Rohe Ergebnisse

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

Niedrigster und höchster Wert entfernt

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

Durchschnittswerte

1068.4  1064.2  1059.6

Beachten Sie, dass dies vorbei ist eine Million Iterationen und die Ergebnisse liegen vor uns 9 Millisekunden im Durchschnitt.Keine wirklich große Optimierung, wenn man bedenkt, dass die meisten iterativen Verarbeitungen in JavaScript über viel kleinere Mengen (z. B. DOM-Container) erfolgen.

Theoretisch wird ein Post-Inkrement-Operator verwendet Mai ein Provisorium herstellen.In der Praxis sind JavaScript-Compiler intelligent genug, um dies zu vermeiden, insbesondere in solch trivialen Fällen.

Betrachten wir zum Beispiel diesen Beispielcode:

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

In diesem Fall produziert der V8-Compiler in NodeJS genau der gleiche Bytecode (siehe insb.bei Opcodes 39-44 für das Inkrement):

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)

Natürlich auch andere JavaScript-Compiler/Interpreter Mai anders machen, aber das ist zweifelhaft.

Als letztes Wort halte ich es trotzdem für eine bewährte Vorgehensweise, nach Möglichkeit die Vorinkrementierung zu verwenden:Da ich häufig die Sprache wechsle, bevorzuge ich die korrekte Syntax semantisch für das, was ich will, anstatt mich auf die Intelligenz des Compilers zu verlassen.Moderne C-Compiler machen beispielsweise auch keinen Unterschied.In C++ kann dies jedoch bei Überladung erhebliche Auswirkungen haben operator++.

Klingt nach vorzeitiger Optimierung.Wenn Sie mit Ihrer App fast fertig sind, überprüfen Sie, wo die Engpässe liegen, und optimieren Sie diese nach Bedarf.Wenn Sie jedoch eine ausführliche Anleitung zur Schleifenleistung wünschen, schauen Sie sich Folgendes an:

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

Aufgrund der Verbesserungen der JS-Engine und der Unterschiede zwischen den Browsern weiß man jedoch nie, wann dies veraltet sein wird.Die beste Wahl ist, sich darüber keine Sorgen zu machen, bis es ein Problem darstellt.Machen Sie Ihren Code klar lesbar.

Bearbeiten:Entsprechend dieser Typ das Pre vs.Beitrag ist statistisch unbedeutend.(wobei pre möglicherweise schlechter ist)

Bei der Optimierung handelt es sich nicht um das Pre-Post-Inkrement.Es handelt sich um die Verwendung von bitweisen „Shift“- und „and“-Operatoren anstelle von „Divide“ und „Mod“.

Es gibt auch die Optimierung, das Javascript zu minimieren, um die Gesamtgröße zu verringern (dies ist jedoch keine Laufzeitoptimierung).

Dies ist wahrscheinlich eine Cargo-Kult-Programmierung.Es sollte keinen Unterschied machen, wenn Sie einen anständigen Compiler/Interpreter für Sprachen verwenden, die keine willkürliche Operatorüberladung aufweisen.

Diese Optimierung war für C++ sinnvoll

T x = ...;
++x

könnte einen Wert an Ort und Stelle ändern, während

T x = ...;
x++

müsste eine Kopie erstellen, indem man so etwas unter der Haube tut

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

Dies kann bei großen Strukturtypen oder bei Typen, die in ihrem Kopierkonstruktor viele Berechnungen durchführen, teuer sein.

Anatoliys Test beinhaltete ein Post-Inkrement innerhalb der Pre-Inkrement-Testfunktion :(

Hier sind die Ergebnisse ohne diese Nebenwirkung...

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

Ausgabe

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

Das ist ein großer Unterschied.

Habe es gerade in Firebug getestet und keinen Unterschied zwischen Post- und Pre-Inkrementen festgestellt.Vielleicht ist diese Optimierung andere Plattformen?Hier ist mein Code für Firebug-Tests:

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

Die Ausgabe lautet:

postIncrement: 140ms
preIncrement: 160ms
postIncrement: 136ms
preIncrement: 157ms
postIncrement: 148ms
preIncrement: 137ms
postIncrement: 136ms
preIncrement: 148ms
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top