Sind Schleifen in umgekehrter Richtung wirklich schneller?

268

Ich habe das schon einige Male gehört. Sind JavaScript-Schleifen beim Rückwärtszählen wirklich schneller? Wenn ja warum? Ich habe einige Beispiele für Testsuiten gesehen, die zeigen, dass umgekehrte Schleifen schneller sind, aber ich kann keine Erklärung dafür finden, warum!

Ich gehe davon aus, dass die Schleife nicht mehr jedes Mal eine Eigenschaft auswerten muss, wenn sie prüft, ob sie fertig ist, und sie prüft nur den endgültigen numerischen Wert.

Dh

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}
djdd87
quelle
6
hehe. das wird auf unbestimmte Zeit dauern. versuchen Sie i--
StampedeXV
5
Die Rückwärtsschleife forist schneller, da die Steuervariable für die obere (hehe, untere) Schleife nicht definiert oder von einem Objekt abgerufen werden muss. es ist eine konstante Null.
Josh Stodola
88
Es gibt keinen wirklichen Unterschied . Native Loop-Konstrukte werden immer sehr schnell sein . Mach dir keine Sorgen über ihre Leistung.
James Allardice
24
@Afshin: Bei Fragen wie diesen zeigen Sie uns bitte die Artikel, auf die Sie sich beziehen.
Leichtigkeitsrennen im Orbit
22
Es gibt Unterschiede, die hauptsächlich für sehr Low-End- und batteriebetriebene Geräte wichtig sind. Der Unterschied besteht darin, dass Sie mit i-- für das Ende der Schleife mit 0 vergleichen, während Sie mit i ++ mit einer Zahl> 0 vergleichen. Ich glaube, der Leistungsunterschied betrug ungefähr 20 Nanosekunden (so etwas wie cmp ax, 0 vs. cmp ax , bx) - das ist nichts anderes als wenn Sie Tausende Male pro Sekunde schleifen, warum nicht 20 Nanosekunden Gewinn für jede :)
luben

Antworten:

902

Es ist nicht i--so schnell wie i++. Eigentlich sind beide gleich schnell.

Was in aufsteigenden Schleifen Zeit braucht, ist die Bewertung ider Größe Ihres Arrays für jede Schleife . In dieser Schleife:

for(var i = array.length; i--;)

Sie werten .lengthnur einmal aus, wenn Sie deklarieren i, während für diese Schleife

for(var i = 1; i <= array.length; i++)

Sie bewerten .lengthjedes Mal i, wenn Sie inkrementieren , wenn Sie prüfen, ob i <= array.length.

In den meisten Fällen sollten Sie sich nicht einmal um diese Art der Optimierung kümmern .

Alestanis
quelle
23
Lohnt es sich, eine Variable für array.length einzuführen und im Kopf der for-Schleife zu verwenden?
Ragatskynet
39
@ragatskynet: Nein, es sei denn, Sie richten einen Benchmark ein und möchten einen Punkt hervorheben.
Jon
29
@ragatskynet Es kommt darauf an: Wird es schneller sein, .lengtheine bestimmte Anzahl von Malen auszuwerten oder eine neue Variable zu deklarieren und zu definieren? In den meisten Fällen handelt es sich um eine vorzeitige (und falsche) Optimierung, es sei denn length, die Bewertung ist sehr teuer.
Alestanis
10
@ Dr.Dredel: Es ist nicht der Vergleich - es ist die Bewertung. 0ist schneller zu bewerten als array.length. Nun, angeblich.
Leichtigkeitsrennen im Orbit
22
Erwähnenswert ist, dass es sich um interpretierte Sprachen wie Ruby, Python handelt. Kompilierte Sprachen, z. B. Java, haben Optimierungen auf Compilerebene, die diese Unterschiede so weit "glätten", dass es keine Rolle spielt, ob sie .lengthdeklariert werden for loopoder nicht.
Haris Krajina
198

Dieser Typ hat viele Schleifen in Javascript in vielen Browsern verglichen. Er hat auch eine Testsuite damit Sie sie selbst ausführen können.

In allen Fällen (es sei denn, ich habe eine beim Lesen verpasst) war die schnellste Schleife:

var i = arr.length; //or 10
while(i--)
{
  //...
}
Tom Ritter
quelle
Schön :) Aber es gibt keine rückwärts gerichtete "for" -Schleife, die getestet wurde ... Aber die von peirix oder searlea erwähnten for-Schleifen sollten ziemlich gleich der "while" -Schleife mit "i--" als Bedingung sein. Und das war die schnellste getestete Schleife.
SvenFinke
11
Interessant, aber ich frage mich, ob die Vorabnahme noch schneller wäre. Da es nicht den Zwischenwert von i speichern muss.
Tvanfosson
Wenn ich mich richtig erinnere, sagte mein Hardware-Kursprofi, dass der Test für 0 oder nicht 0 die schnellstmögliche "Berechnung" ist. Während (i--) ist der Test immer ein Test für 0. Vielleicht ist dies der Grund, warum dies am schnellsten ist?
StampedeXV
3
@tvanfosson, wenn Sie vorab dekrementieren, --idann müssen Sie var current = arr[i-1];innerhalb der Schleife verwenden, oder es wird um eins ausgeschaltet ...
DaveAlger
2
Ich habe gehört, dass i-- schneller sein kann als --i, weil im zweiten Fall der Prozessor dekrementieren und dann gegen den neuen Wert testen muss (es besteht eine Datenabhängigkeit zwischen den Anweisungen), während dies im ersten Fall der Prozessor kann Testen Sie den vorhandenen Wert und verringern Sie ihn einige Zeit später. Ich bin mir nicht sicher, ob dies für JavaScript oder nur für C-Code auf sehr niedriger Ebene gilt.
Marcus
128

Ich versuche mit dieser Antwort ein breites Bild zu vermitteln.

Die folgenden Gedanken in Klammern waren meine Überzeugung, bis ich das Problem erst kürzlich getestet habe:

[[In Bezug auf Low-Level-Sprachen wie C / C ++ wird der Code so kompiliert, dass der Prozessor einen speziellen bedingten Sprungbefehl hat, wenn eine Variable Null (oder nicht Null) ist.
Wenn Sie sich für so viel Optimierung interessieren, können Sie ++istattdessen auch gehen i++, da dies ++iein einzelner Prozessorbefehl ist, während i++bedeutet j=i+1, i=j.]]

Wirklich schnelle Schleifen können durch Abrollen erstellt werden:

for(i=800000;i>0;--i)
    do_it(i);

Es kann viel langsamer sein als

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

Die Gründe hierfür können jedoch recht kompliziert sein (nur um zu erwähnen, dass es im Spiel Probleme mit der Vorverarbeitung von Prozessorbefehlen und der Cache-Behandlung gibt).

In Bezug auf Hochsprachen wie JavaScript können Sie die Dinge optimieren, wenn Sie sich auf Bibliotheken und integrierte Funktionen für Schleifen verlassen. Lassen Sie sie entscheiden, wie es am besten gemacht wird.

Folglich würde ich in JavaScript vorschlagen, so etwas wie zu verwenden

array.forEach(function(i) {
    do_it(i);
});

Es ist auch weniger fehleranfällig und Browser haben die Möglichkeit, Ihren Code zu optimieren.

[BEMERKUNG: Nicht nur die Browser, sondern auch Sie haben einen Platz zum einfachen Optimieren. Definieren Sie die forEachFunktion einfach neu (browserabhängig), damit die neuesten Tricks verwendet werden! :) @AMK sagt in besonderen Fällen, dass es sich lohnt, lieber array.popoder zu verwenden array.shift. Wenn Sie das tun, stellen Sie es hinter den Vorhang. Der größte Overkill besteht darin, Optionen forEachzur Auswahl des Schleifenalgorithmus hinzuzufügen .]

Darüber hinaus empfiehlt es sich, auch für einfache Sprachen, eine intelligente Bibliotheksfunktion für komplexe Schleifenoperationen zu verwenden, wenn dies möglich ist.

Diese Bibliotheken können auch Dinge (Multithreading) hinter Ihren Rücken legen, und auch spezialisierte Programmierer halten sie auf dem neuesten Stand.

Ich habe etwas genauer nachgesehen und es stellt sich heraus, dass es in C / C ++ selbst bei 5e9 = (50.000x100.000) Operationen keinen Unterschied zwischen Auf und Ab gibt, wenn die Tests gegen eine Konstante durchgeführt werden, wie @alestanis sagt. (JsPerf-Ergebnisse sind manchmal inkonsistent, sagen aber im Großen und Ganzen dasselbe: Sie können keinen großen Unterschied machen.)
Das --iist also eher eine "noble" Sache. Sie sehen nur wie ein besserer Programmierer aus. :) :)

Andererseits hat mich das Abrollen in dieser 5e9-Situation von 12 Sekunden auf 2,5 Sekunden, wenn ich um 10 Sekunden ging, und auf 2,1 Sekunden, wenn ich um 20 Sekunden ging, gesenkt. Es war ohne Optimierung, und die Optimierung hat die Dinge auf unermessliche Zeit reduziert. :) (Das Abrollen kann auf meine oben beschriebene Weise oder mithilfe von erfolgen i++, aber das bringt in JavaScript keine Fortschritte .)

Alles in allem: Behalten Sie i--/ i++und ++i/ oder i++Unterschiede zu den Vorstellungsgesprächen bei, halten Sie sich an array.forEachoder andere komplexe Bibliotheksfunktionen, sofern verfügbar. ;)

Barney
quelle
18
Das Schlüsselwort ist "kann sein". Ihre abgewickelte Schleife ist möglicherweise auch langsamer als die ursprüngliche. Messen Sie bei der Optimierung immer, damit Sie genau wissen, welche Auswirkungen Ihre Änderungen hatten.
Jalf
1
@ Jalf wahr in der Tat, +1. Unterschiedliche Abwicklungslängen (> = 1) unterscheiden sich in der Effizienz. Aus diesem Grund ist es bequemer, diesen Job nach Möglichkeit den Bibliotheken zu überlassen, ganz zu schweigen davon, dass Browser auf unterschiedlichen Architekturen ausgeführt werden. Daher ist es möglicherweise besser, wenn sie entscheiden, wie sie vorgehen sollen array.each(...). Ich glaube nicht, dass sie versuchen würden, mit einfachen For-Loops zu experimentieren.
Barney
1
@BarnabasSzabolcs Die Frage bezieht sich speziell auf JavaScript, nicht auf C oder andere Sprachen. In JS gibt es einen Unterschied, siehe meine Antwort unten. Obwohl nicht auf die Frage anwendbar, +1 gute Antwort!
AMK
1
Und los geht's - +1um dir ein Great AnswerAbzeichen zu besorgen . Wirklich tolle Antwort. :)
Rohit Jain
1
APL / A ++ ist auch keine Sprache. Die Leute benutzen diese, um auszudrücken, dass sie nicht an Sprachspezifikationen interessiert sind, sondern an etwas, das diese Sprachen teilen.
Barney
33

i-- ist so schnell wie i++

Dieser Code ist genauso schnell wie Ihr Code, verwendet jedoch eine zusätzliche Variable:

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

Es wird empfohlen, die Größe des Arrays NICHT jedes Mal zu bewerten. Bei großen Arrays kann man den Leistungsabfall sehen.

sainiuc
quelle
6
Sie liegen hier eindeutig falsch. Jetzt benötigt die Schleifensteuerung eine zusätzliche interne Variable (i und höher). Abhängig von der JavaScript-Engine kann dies das Potenzial zur Optimierung des Codes einschränken. JITs übersetzen einfache Schleifen in direkte Maschinen-Opcodes. Wenn Schleifen jedoch zu viele Variablen enthalten, kann JIT nicht so gut optimieren. Im Allgemeinen ist es durch die von JIT verwendeten Architektur- oder CPU-Register begrenzt. Einmal initialisieren und einfach die sauberste Lösung runtergehen und deshalb wird es überall empfohlen.
Pavel P
Die zusätzliche Variable wird vom Optimierer eines gemeinsamen Interpreters / Compilers entfernt. Der Punkt ist der 'Vergleich mit 0' - siehe meine Antwort für weitere Erklärungen.
H.-Dirk Schmitt
1
Besser 'up' in die Schleife setzen:for (var i=0, up=Things.length; i<up; i++) {}
thdoan
22

Da Sie sich für das Thema interessieren, werfen Sie einen Blick auf Greg Reimers Weblog-Beitrag über einen JavaScript-Loop-Benchmark. Was ist der schnellste Weg, um eine Loop in JavaScript zu codieren? ::

Ich habe eine Loop-Benchmarking-Testsuite für verschiedene Arten der Codierung von Loops in JavaScript erstellt. Es gibt bereits einige davon, aber ich habe keine gefunden, die den Unterschied zwischen nativen Arrays und HTML-Sammlungen bestätigt.

Sie können auch einen Leistungstest für eine Schleife durchführen, indem Sie diese öffnen https://blogs.oracle.com/greimer/resource/loop-test.html(funktioniert nicht, wenn JavaScript im Browser beispielsweise durch NoScript blockiert wird ).

BEARBEITEN:

Ein neuerer Benchmark von Milan Adamovsky kann hier zur Laufzeit durchgeführt werden zur für verschiedene Browser durchgeführt werden.

Für einen Test in Firefox 17.0 unter Mac OS X 10.6 habe ich folgende Schleife erhalten:

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

als der schnellste vorangegangen von:

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

Benchmark-Beispiel.

Traumcrash
quelle
5
Dieser Beitrag ist jetzt 4 Jahre alt. Angesichts der Geschwindigkeit (ha!), Mit der JavaScript-Engines aktualisiert wurden, sind die meisten Ratschläge wahrscheinlich veraltet - in dem Artikel wird Chrome nicht einmal erwähnt und es handelt sich um eine glänzende V8-JavaScript-Engine.
Yi Jiang
Ja, ich vermisse dieses Detail, danke, dass Sie darauf hingewiesen haben. Zu diesem Zeitpunkt gab es keinen großen Unterschied zwischen --i oder i ++ in der Schleife für.
Dreamcrash
19

Es ist nicht das --oder ++, es ist die Vergleichsoperation. Mit können --Sie einen Vergleich mit 0 verwenden, während ++Sie ihn mit der Länge vergleichen müssen. Auf dem Prozessor ist normalerweise ein Vergleich mit Null verfügbar, während ein Vergleich mit einer endlichen ganzen Zahl eine Subtraktion erfordert.

a++ < length

ist eigentlich kompiliert als

a++
test (a-length)

Auf dem Prozessor dauert das Kompilieren also länger.

Dr. BDO Adams
quelle
15

Kurze Antwort

Für normalen Code, insbesondere in einer Hochsprache wie JavaScript , gibt es keinen Leistungsunterschied in i++und i--.

Das Leistungskriterium ist die Verwendung in der forSchleife und der Vergleich .

Dies gilt für alle Hochsprachen und ist weitgehend unabhängig von der Verwendung von JavaScript. Die Erklärung ist der resultierende Assembler-Code in der unteren Zeile.

Ausführliche Erklärung

In einer Schleife kann ein Leistungsunterschied auftreten. Der Hintergrund ist, dass Sie auf der Assembler-Codeebene sehen können, dass acompare with 0 nur eine Anweisung ist, für die kein zusätzliches Register erforderlich ist.

Dieser Vergleich wird bei jedem Durchlauf der Schleife ausgegeben und kann zu einer messbaren Leistungsverbesserung führen.

for(var i = array.length; i--; )

wird zu einem Pseudocode wie folgt ausgewertet :

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

Beachten Sie, dass 0 ein Literal oder mit anderen Worten ein konstanter Wert ist.

for(var i = 0 ; i < array.length; i++ )

wird zu einem Pseudocode wie diesem ausgewertet (normale Interpreteroptimierung angenommen):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

Beachten Sie, dass end eine Variable ist, die ein CPU- Register benötigt. Dies kann zu einem zusätzlichen Registeraustausch im Code führen und erfordert eine teurere Vergleichsanweisung in der ifAnweisung.

Nur meine 5 Cent

Für eine Hochsprache ist die Lesbarkeit, die die Wartbarkeit erleichtert, als geringfügige Leistungsverbesserung wichtiger.

Normalerweise ist die klassische Iteration vom Anfang bis zum Ende des Arrays besser.

Die schnellere Iteration vom Array- Ende bis zum Start führt zu der möglicherweise unerwünschten umgekehrten Reihenfolge.

Post scriptum

Wie in einem Kommentar gefragt: Der Unterschied von --iundi-- liegt in der Bewertung voni vor oder nach dem Dekrementieren.

Die beste Erklärung ist, es auszuprobieren ;-) Hier ist ein Bash- Beispiel.

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9
H.-Dirk Schmitt
quelle
1
1+ Gute Erklärung. Könnten Sie bitte den Unterschied zwischen --iund i--auch erklären ?
Afshin Mehrabani
14

Ich habe die gleiche Empfehlung in Sublime Text 2 gesehen.

Wie bereits gesagt, besteht die Hauptverbesserung nicht darin, die Länge des Arrays bei jeder Iteration in der for-Schleife zu bewerten. Dies ist eine bekannte Optimierungstechnik und besonders effizient in JavaScript , wenn die Array - Teil des HTML - Dokuments ist (dabei ein forfür die alle dieli Elemente ausgeführt wird).

Zum Beispiel,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

ist viel langsamer als

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

Aus meiner Sicht ist die Hauptverbesserung des Formulars in Ihrer Frage die Tatsache, dass es keine zusätzliche Variable deklariert ( lenin meinem Beispiel).

Aber wenn Sie mich fragen, geht es nicht um die i++vs- i--Optimierung, sondern darum, dass Sie nicht bei jeder Iteration die Länge des Arrays bewerten müssen (Sie können einen Benchmark-Test auf jsperf sehen ).

BBog
quelle
1
Ich muss eine Ausnahme von dem Wort machen , das hier berechnet wird . Siehe meinen Kommentar zu Pavel's Antwort . Die ECMA-Spezifikation besagt, dass die Array-Länge nicht berechnet wird, wenn Sie darauf verweisen.
Kojiro
Wäre "Bewerten" eine bessere Wahl? Interessante Tatsache übrigens, ich wusste das nicht
BBog
Die zusätzliche Variable wird vom Optimierer eines gemeinsamen Interpreters / Compilers entfernt. Gleiches gilt für die Auswertung von array.length. Der Punkt ist der 'Vergleich mit 0' - siehe meine Antwort für weitere Erklärungen.
H.-Dirk Schmitt
@ H.-DirkSchmitt Ihre vernünftige Antwort beiseite, lange in der Geschichte von JavaScript hat der Compiler die Leistungskosten der Suchkette nicht optimiert. AFAIK V8 war der erste, der es versuchte.
Kojiro
@ H.-DirkSchmitt genau wie Kojiro sagte, ist dies ein bekannter und etablierter Trick. Auch wenn es in modernen Browsern nicht mehr relevant ist, macht es es dennoch nicht obsolet. Außerdem ist es immer noch der beste Weg, imo, entweder eine neue Variable für die Länge einzuführen oder den Trick in OPs Frage zu verwenden. Es ist nur eine kluge Sache und eine gute Praxis. Ich glaube nicht, dass dies etwas damit zu tun hat, dass der Compiler so optimiert wurde, dass er sich um etwas kümmert, das in JS
BBog
13

Ich denke nicht, dass es Sinn macht zu sagen, dass i--das schneller gehti++ in JavaScript.

Zuerst hängt es völlig von der Implementierung der JavaScript-Engine ab.

Zweitens hängt i++vs i--vollständig von der CPU ab, die es ausführt , vorausgesetzt, dass einfachste Konstrukte JIT'ed und in native Anweisungen übersetzt werden . Das heißt, auf ARMs (Mobiltelefonen) ist es schneller, auf 0 zu fallen, da Dekrementieren und Vergleichen mit Null in einem einzigen Befehl ausgeführt werden.

Wahrscheinlich haben Sie gedacht, dass einer verschwenderischer ist als der andere, weil der vorgeschlagene Weg dies ist

for(var i = array.length; i--; )

aber vorgeschlagener Weg ist nicht, weil einer schneller als der andere, sondern einfach, weil, wenn Sie schreiben

for(var i = 0; i < array.length; i++)

dann bei jeder Iteration array.length ausgewertet werden (eine intelligentere JavaScript-Engine könnte möglicherweise herausfinden, dass die Schleife die Länge des Arrays nicht ändert). Obwohl es wie eine einfache Anweisung aussieht, ist es tatsächlich eine Funktion, die von der JavaScript-Engine unter der Haube aufgerufen wird.

Der andere Grund, warum i--dies als "schneller" angesehen werden könnte, ist, dass die JavaScript-Engine nur eine interne Variable zuweisen muss, um die Schleife zu steuern (Variable zu var i). Wenn Sie mit array.length oder einer anderen Variablen verglichen haben, musste es mehr als eine interne Variable geben, um die Schleife zu steuern, und die Anzahl der internen Variablen ist ein begrenztes Kapital einer JavaScript-Engine. Je weniger Variablen in einer Schleife verwendet werden, desto größer ist die Chance, dass JIT optimiert wird. Deshalb i--könnte man schneller denken ...

Pavel P.
quelle
3
Es lohnt sich wahrscheinlich, sorgfältig zu formulieren, wie array.lengthbewertet wird. Die Länge wird nicht berechnet, wenn Sie darauf verweisen. (Es ist nur eine Eigenschaft, die festgelegt wird, wenn ein Array-Index erstellt oder geändert wird. ) Wenn zusätzliche Kosten anfallen, liegt dies daran, dass die JS-Engine die Suchkette für diesen Namen nicht optimiert hat.
Kojiro
Nun, ich bin mir nicht sicher, was die Ecma-Spezifikation sagt, aber es ist nicht einfach, einige Interna verschiedener JavaScript-Engines zu kennen, getLength(){return m_length; }da es sich um eine gewisse Haushaltsführung handelt. Aber wenn Sie versuchen, rückwärts zu denken: Das wäre ziemlich genial, um eine Array-Implementierung zu schreiben, bei der die Länge tatsächlich berechnet werden müsste :)
Pavel P
Die ECMA-Spezifikation verlangt, dass die Längeneigenschaft bereits berechnet ist. Das lengthmuss sofort aktualisiert werden, wenn eine Eigenschaft, die ein Array-Index ist, hinzugefügt oder geändert wird.
Kojiro
Ich versuche zu sagen, dass es ziemlich schwer ist, die Spezifikation zu verletzen, wenn Sie versuchen, darüber nachzudenken.
Pavel P
1
x86 ist in dieser Hinsicht wie ARM. dec/jnzvs. inc eax / cmp eax, edx / jne.
Peter Cordes
13

Da keine der anderen Antworten Ihre spezifische Frage zu beantworten scheint (mehr als die Hälfte von ihnen zeigt C-Beispiele und diskutiert Sprachen auf niedrigerer Ebene, Ihre Frage bezieht sich auf JavaScript), habe ich beschlossen, meine eigene zu schreiben.

Also los geht's:

Einfache Antwort: Ist i-- im Allgemeinen schneller, da nicht jedes Mal ein Vergleich mit 0 durchgeführt werden muss. Die Testergebnisse für verschiedene Methoden sind unten aufgeführt:

Testergebnisse: Wie von diesem jsPerf "bewiesen" , arr.pop()ist es tatsächlich die mit Abstand schnellste Schleife. Aber, die sich auf --i, i--, i++und++i wie Sie in Ihrer Frage gestellt, hier sind jsPerf (sie von mehreren jsPerf der, beachten Sie bitte Quellen siehe unten) Ergebnisse zusammengefasst:

--iund i--sind in Firefox gleich, während i--es in Chrome schneller ist.

In Chrome ist ein Basic für loop ( for (var i = 0; i < arr.length; i++)) schneller als i--und --iin Firefox langsamer.

Sowohl in Chrome als auch in Firefox wird zwischengespeichert arr.length mit Chrome um etwa 170.000 Ops / Sek. Deutlich schneller.

Ohne signifikanten Unterschied ++iist i++AFAIK schneller als in den meisten Browsern, in keinem Browser ist es umgekehrt.

Kürzere Zusammenfassung: arr.pop() ist bei weitem die schnellste Schleife; für die speziell genannten Schleifen i--ist die schnellste Schleife.

Quellen: http://jsperf.com/fastest-array-loops-in-javascript/15 , http://jsperf.com/ipp-vs-ppi-2

Ich hoffe das beantwortet deine Frage.

AMK
quelle
1
Es scheint, dass Ihr popTest nur so schnell zu sein scheint, weil er die Array-Größe für den größten Teil der Schleife auf 0 reduziert - soweit ich das beurteilen kann. Um jedoch sicherzustellen, dass die Dinge fair sind, habe ich diesen jsperf so entworfen, dass er das Array bei jedem Test auf die gleiche Weise erstellt - was sich .shift()als Gewinner für meine wenigen Browser herausstellt
Pebbl
Upvoted für die einzige Erwähnung ++i: D
jaggedsoft
12

Dies hängt von der Platzierung Ihres Arrays im Speicher und der Trefferquote der Speicherseiten ab, während Sie auf dieses Array zugreifen.

In einigen Fällen ist der Zugriff auf Array-Mitglieder in Spaltenreihenfolge aufgrund der Erhöhung der Trefferquote schneller als in der Zeilenreihenfolge.

Akbar Bayati
quelle
1
Wenn nur das OP gefragt hätte, warum das Durchlaufen derselben Matrix in unterschiedlichen
Reihenfolgen
1
Wenn man bedenkt, dass in Betriebssystemen die Speicherverwaltung auf Paging basiert und ein Prozess Daten benötigt, die sich nicht auf zwischengespeicherten Seiten befinden, tritt im Betriebssystem ein Seitenfehler auf, und die Zielseite muss in den CPU-Cache gebracht und durch eine andere Seite ersetzt werden. verursacht Overhead bei der Verarbeitung, der länger dauert als wenn sich die Zielseite im CPU-Cache befindet. Angenommen, wir haben ein großes Array definiert, bei dem jede Zeile größer als die Größe des Seitenbetriebssystems ist und wir in der Reihenfolge der Zeilen darauf zugreifen. In diesem Fall steigt die Seitenfehlerquote und das Ergebnis ist langsamer als der Zugriff auf dieses Array in der Spaltenreihenfolge.
Akbar Bayati
Seitenfehler sind nicht dasselbe wie Cache-Fehler. Sie haben nur einen Seitenfehler, wenn Ihr Speicher auf die Festplatte ausgelagert wurde. Der Zugriff auf mehrdimensionale Arrays in sequenzieller Reihenfolge, in der sie im Speicher gespeichert sind, ist aufgrund der Cache-Lokalität (wobei alle Bytes einer Cache-Zeile beim Abrufen verwendet werden) schneller als aufgrund von Seitenfehlern. (Es sei denn, Ihr Arbeitssatz ist zu groß für den Computer, den Sie verwenden.)
Peter Cordes
10

Das letzte Mal habe ich mich darum gekümmert, als ich eine 6502- Assembly geschrieben habe (8-Bit, ja!). Der große Vorteil besteht darin, dass die meisten arithmetischen Operationen (insbesondere Dekremente) eine Reihe von Flags aktualisiert haben. Eines davon war Zder Indikator "erreicht Null".

Am Ende der Schleife haben Sie also nur zwei Anweisungen ausgeführt: DEC(Dekrementieren) und JNZ(Springen, wenn nicht Null), kein Vergleich erforderlich!

Javier
quelle
Im Fall von JavaScript gilt dies offensichtlich nicht (da es auf CPUs ausgeführt wird, die keine solchen Op-Codes haben). Der wahre Grund für das i--vs i++ist höchstwahrscheinlich, dass Sie mit dem ersteren keine zusätzlichen Steuervariablen in den Bereich der Schleife einführen. Siehe meine Antwort unten ...
Pavel P
1
richtig, es trifft wirklich nicht zu; Aber es ist ein sehr verbreiteter C-Stil, und für diejenigen von uns, die sich daran gewöhnt haben, sieht er sauberer aus. :-)
Javier
x86 und ARM sind in dieser Hinsicht beide wie 6502. dec/jnzanstelle inc/cmp/jnefür den x86-Fall. Eine leere Schleife wird nicht schneller ausgeführt (beide sättigen den Zweigdurchsatz), aber durch Herunterzählen wird der Schleifen-Overhead geringfügig reduziert. Aktuelle Intel HW-Prefetchers stören sich auch nicht an Speicherzugriffsmustern, die in absteigender Reihenfolge ablaufen. Ältere CPUs könnten meiner Meinung nach 4 Rückwärtsströme und 6 oder 10 Vorwärtsströme, IIRC, verfolgen.
Peter Cordes
7

Dies kann dadurch erklärt werden, dass JavaScript (und alle Sprachen) schließlich in Opcodes umgewandelt werden, die auf der CPU ausgeführt werden. CPUs haben immer einen einzigen Befehl zum Vergleichen mit Null, was verdammt schnell ist.

Als Nebenwirkung , wenn Sie garantieren können , countist immer >= 0, Sie vereinfachen könnte:

for (var i = count; i--;)
{
  // whatever
}
Searlea
quelle
1
Kürzerer Quellcode bedeutet nicht unbedingt, dass er schneller ist. Hast du es gemessen?
Greg Hewgill
Hoppla, ich habe diesen verpasst. Nagel auf den Kopf da Kap.
Robert
1
Ich würde gerne die Assembly-Quellen sehen, bei denen der Vergleich mit 0 unterschiedlich ist. Es ist ein paar Jahre her, aber einmal habe ich eine Menge Assembler-Codierung durchgeführt und ich kann mir für mein ganzes Leben keine Möglichkeit vorstellen, einen Vergleich / Test gegen 0 auf eine Weise durchzuführen, die nicht gleich schnell sein kann für jede andere ganze Zahl. Doch was Sie sagen, klingt wahr. Frustriert, dass ich nicht herausfinden kann warum!
Brian Knoblauch
7
@Brian Knoblauch: Wenn Sie eine Anweisung wie "dec eax" (x86-Code) verwenden, setzt diese Anweisung automatisch das Z (Null) -Flag, das Sie sofort testen können, ohne dazwischen eine andere Vergleichsanweisung verwenden zu müssen.
Greg Hewgill
1
Bei der Interpretation von Javascript bezweifle ich jedoch, dass Opcodes der Engpass sein werden. Wahrscheinlicher ist, dass weniger Token bedeuten, dass der Interpreter den Quellcode schneller verarbeiten kann.
Todd Owen
6

Dies hängt nicht vom Vorzeichen --oder ab ++, sondern von den Bedingungen, die Sie in der Schleife anwenden.

Beispiel: Ihre Schleife ist schneller, wenn die Variable einen statischen Wert hat, als wenn Ihre Schleife jedes Mal Bedingungen wie die Länge eines Arrays oder andere Bedingungen überprüft.

Aber machen Sie sich keine Sorgen um diese Optimierung, da diesmal der Effekt in Nanosekunden gemessen wird.

Arvind Kanjariya
quelle
Mehrere Nanosekunden-Schleifen können zu Sekunden werden ... Es ist nie eine schlechte Idee, zu optimieren, wenn Sie Zeit dafür haben
Buksy
6

for(var i = array.length; i--; )ist nicht viel schneller. Aber wenn Sie ersetzen array.lengthmit super_puper_function(), sein , dass möglicherweise erheblich schneller (da es in jeder Iteration genannt wird ). Das ist der Unterschied.

Wenn Sie es 2014 ändern möchten, müssen Sie nicht über Optimierung nachdenken. Wenn Sie es mit "Suchen & Ersetzen" ändern möchten, müssen Sie nicht über Optimierung nachdenken. Wenn Sie keine Zeit haben, müssen Sie nicht über Optimierung nachdenken. Aber jetzt haben Sie Zeit, darüber nachzudenken.

PS: i--ist nicht schneller als i++.

Dmitry Isaev
quelle
6

Um es kurz zu machen: In JavaScript gibt es absolut keinen Unterschied.

Zunächst können Sie es selbst testen:

Sie können nicht nur jedes Skript in einer beliebigen JavaScript-Bibliothek testen und ausführen, sondern haben auch Zugriff auf die gesamte Reihe zuvor geschriebener Skripte sowie auf die Fähigkeit, Unterschiede zwischen der Ausführungszeit in verschiedenen Browsern auf verschiedenen Plattformen festzustellen.

Soweit Sie sehen, gibt es in keiner Umgebung einen Unterschied zwischen der Leistung.

Wenn Sie die Leistung Ihres Skripts verbessern möchten, können Sie Folgendes versuchen:

  1. Haben Sie eine var a = array.length;Anweisung, damit Sie ihren Wert nicht jedes Mal in der Schleife berechnen
  2. Rollen Sie die Schleife ab http://en.wikipedia.org/wiki/Loop_unwinding

Aber Sie müssen verstehen, dass die Verbesserung, die Sie erzielen können, so unbedeutend ist, dass Sie sich meistens nicht einmal darum kümmern sollten.

Meine eigene Meinung, warum ein solches Missverständnis (Dec vs Inc) aufgetreten ist

Vor langer, langer Zeit gab es eine gemeinsame Maschinenanweisung, DSZ (Decrement and Skip on Zero). Personen, die in Assemblersprache programmiert haben, haben diese Anweisung verwendet, um Schleifen zu implementieren, um ein Register zu speichern. Jetzt sind diese alten Fakten überholt, und ich bin mir ziemlich sicher, dass Sie mit dieser Pseudoverbesserung in keiner Sprache eine Leistungsverbesserung erzielen werden.

Ich denke, der einzige Weg, wie sich solches Wissen in unserer Zeit verbreiten kann, ist, wenn Sie den Personencode eines anderen lesen. Sehen Sie sich eine solche Konstruktion an und fragen Sie, warum sie implementiert wurde. Hier die Antwort: "Sie verbessert die Leistung, weil sie mit Null verglichen wird." Sie waren verwirrt über das höhere Wissen Ihres Kollegen und denken, es zu nutzen, um viel schlauer zu sein :-)

Salvador Dali
quelle
Interessant, aber für Ihre Tests unter Firefox 16.0.2 unter Win7 war die Dekrementierungsschleife 29% langsamer ... BEARBEITEN: Ignorieren Sie das. Wiederholte Tests erwiesen sich als nicht schlüssig. Meine Testergebnisse für nachfolgende Läufe enthalten überraschend viel Rauschen. Ich weiß nicht genau warum.
Ja, ich habe versucht, das zu erklären, indem ich alles andere geschlossen und nur die Tests ausgeführt habe. Immer noch wackelige Ergebnisse. Sehr eigenartig.
Ich denke, Sie haben den eigentlichen Punkt verpasst, warum es in JavaScript besser ist, auf Null zu gehen. Dies liegt hauptsächlich daran, dass auf diese Weise nur eine Variable die Schleifenausführung steuert, z. B. dass Optimierer / JITer auf diese Weise mehr Raum für Verbesserungen bietet. Die Verwendung array.lengthführt nicht unbedingt zu Leistungseinbußen, da die virtuelle JS-Maschine intelligent genug ist, um herauszufinden, ob das Array nicht vom Hauptteil der Schleife geändert wird. Siehe meine Antwort unten.
Pavel P
1
Nitpicking: Diese alte Tatsache (Optimierungen der Assemblersprache) ist nicht veraltet, sondern nur arkan. wie in müssen Sie nicht wissen, es sei denn, Sie tun es wirklich. :-)
Javier
6

Ich habe einen Vergleich auf jsbench gemacht .

Wie Alestani betonte, besteht eine Sache, die in aufsteigenden Schleifen einige Zeit in Anspruch nimmt, darin, für jede Iteration die Größe Ihres Arrays zu bewerten. In dieser Schleife:

for ( var i = 1; i <= array.length; i++ )

Sie bewerten .lengthjedes Mal, wenn Sie inkrementieren i. In diesem:

for ( var i = 1, l = array.length; i <= l; i++ )

Sie bewerten .lengthnur einmal, wenn Sie deklarieren i. In diesem:

for ( var i = array.length; i--; )

Der Vergleich ist implizit, erfolgt kurz vor dem Dekrementieren iund der Code ist sehr gut lesbar. Was jedoch einen großen Unterschied machen kann, ist das, was Sie in die Schleife einfügen.

Schleife mit Funktionsaufruf (an anderer Stelle definiert):

for (i = values.length; i-- ;) {
  add( values[i] );
}

Schleife mit Inline-Code:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

Wenn Sie Ihren Code einbinden können, anstatt eine Funktion aufzurufen, ohne die Lesbarkeit zu beeinträchtigen, können Sie eine Schleife um eine Größenordnung schneller erstellen!


Hinweis : Da der Browser einfache Funktionen immer besser einbinden kann, hängt dies wirklich davon ab, wie komplex Ihr Code ist. Also, Profil vor der Optimierung, weil

  1. Der Engpass kann an anderer Stelle liegen (Ajax, Reflow, ...)
  2. Sie können einen besseren Algorithmus wählen
  3. Sie können eine bessere Datenstruktur wählen

Aber erinnere dich:

Code ist so geschrieben, dass Benutzer ihn lesen können, und nur im Übrigen, damit Maschinen ausgeführt werden können.

Spyryto
quelle
+1 für diese Antwort und für den Benchmark. Ich habe forEach-Tests hinzugefügt und den Benchmark zu einer eigenständigen Datei überarbeitet, die sowohl im Browser als auch im Knoten ausgeführt werden kann. jsfiddle.net/hta69may/2 . Für den Knoten ist "Umkehrschleife, impliziter Vergleich, Inline-Code" am schnellsten. Tests in FF 50 zeigten jedoch merkwürdige Ergebnisse: Nicht nur die Timings waren fast zehnmal kürzer (!), Sondern beide "forEach" -Tests waren so schnell wie "Reverse Loop". Vielleicht sollten Node-Leute Mozillas JS-Engine anstelle von V8 verwenden? :)
Fr0sT
4

Die Art und Weise, wie Sie es jetzt tun, ist nicht schneller (abgesehen davon, dass es sich um eine unbestimmte Schleife handelt, haben Sie das wohl vorgehabt i--.

Wenn Sie es schneller machen möchten, tun Sie Folgendes:

for (i = 10; i--;) {
    //super fast loop
}

Natürlich würden Sie es auf einer so kleinen Schleife nicht bemerken. Der Grund, warum es schneller ist, ist, dass Sie i dekrementieren, während Sie überprüfen, ob es "wahr" ist (es wird als "falsch" ausgewertet, wenn es 0 erreicht).

Peirix
quelle
1
Vermissen Sie ein Semikolon? (i = 10; i--;)
Searlea
Ja ja, ich habe den Fehler behoben. Und wenn wir wählerisch werden, werde ich darauf hinweisen, dass Sie Ihr Semikolon nach Ihrem i-- vergessen haben! Heh.
djdd87
Warum sollte das schneller sein? Eine kürzere Quelle bedeutet nicht, dass es schneller sein wird. Hast du es gemessen?
Greg Hewgill
4
Ja, ich habe es gemessen und ich sage nicht, dass eine kürzere Quelle es schneller macht, aber weniger Operationen machen es schneller.
Peirix
4

Manchmal können geringfügige Änderungen an der Art und Weise, wie wir unseren Code schreiben, einen großen Unterschied darin machen, wie schnell unser Code tatsächlich ausgeführt wird. Ein Bereich, in dem eine geringfügige Codeänderung einen großen Unterschied zu den Ausführungszeiten bewirken kann, ist der Bereich, in dem eine for-Schleife ein Array verarbeitet. Wenn das Array aus Elementen auf der Webseite besteht (z. B. Optionsfelder), hat die Änderung den größten Effekt. Es lohnt sich jedoch, diese Änderung auch dann anzuwenden, wenn sich das Array im Javascript-Code befindet.

Die herkömmliche Art der Codierung einer for-Schleife zur Verarbeitung eines Arrays lautet wie folgt:

for (var i = 0; i < myArray.length; i++) {...

Das Problem dabei ist, dass das Auswerten der Länge des Arrays mit myArray.length Zeit braucht und die Art und Weise, wie wir die Schleife codiert haben, bedeutet, dass diese Auswertung jedes Mal um die Schleife herum durchgeführt werden muss. Wenn das Array 1000 Elemente enthält, wird die Länge des Arrays 1001 Mal ausgewertet. Wenn wir Optionsfelder betrachtet haben und myForm.myButtons.length hatten, dauert die Auswertung noch länger, da die entsprechende Gruppe von Schaltflächen innerhalb des angegebenen Formulars zuerst gefunden werden muss, bevor die Länge jedes Mal um die Schleife herum ausgewertet werden kann.

Offensichtlich erwarten wir nicht, dass sich die Länge des Arrays während der Verarbeitung ändert, sodass all diese Neuberechnungen der Länge die Verarbeitungszeit nur unnötig verlängern. (Wenn Sie Code in der Schleife haben, der Array-Einträge hinzufügt oder entfernt, kann sich die Array-Größe zwischen den Iterationen ändern, sodass wir den Code, der darauf testet, nicht ändern können.)

Um dies für eine Schleife zu korrigieren, bei der die Größe festgelegt ist, müssen wir die Länge einmal am Anfang der Schleife auswerten und in einer Variablen speichern. Wir können dann die Variable testen, um zu entscheiden, wann die Schleife beendet werden soll. Dies ist viel schneller als die Auswertung der Array-Länge jedes Mal, insbesondere wenn das Array mehr als nur wenige Einträge enthält oder Teil der Webseite ist.

Der Code dafür lautet:

for (var i = 0, var j = myArray.length; i < j; i++) {...

Jetzt bewerten wir die Größe des Arrays nur noch einmal und testen unseren Schleifenzähler anhand der Variablen, die diesen Wert jedes Mal um die Schleife herum enthält. Auf diese zusätzliche Variable kann viel schneller zugegriffen werden als durch Auswerten der Größe des Arrays, sodass unser Code viel schneller als zuvor ausgeführt wird. Wir haben nur eine zusätzliche Variable in unserem Skript.

Oft spielt es keine Rolle, in welcher Reihenfolge wir das Array verarbeiten, solange alle Einträge im Array verarbeitet werden. In diesem Fall können wir unseren Code etwas schneller machen, indem wir die gerade hinzugefügte zusätzliche Variable entfernen und das Array in umgekehrter Reihenfolge verarbeiten.

Der endgültige Code, der unser Array so effizient wie möglich verarbeitet, lautet:

for (var i = myArray.length-1; i > -1; i--) {...

Dieser Code wertet die Größe des Arrays immer noch nur einmal zu Beginn aus, aber anstatt den Schleifenzähler mit einer Variablen zu vergleichen, vergleichen wir ihn mit einer Konstanten. Da der Zugriff auf eine Konstante noch effektiver ist als auf eine Variable und wir eine Zuweisungsanweisung weniger haben als zuvor, ist unsere dritte Version des Codes jetzt etwas effizienter als die zweite Version und weitaus effizienter als die erste.

Sid
quelle
4

++vs. --spielt keine Rolle, da JavaScript eine interpretierte Sprache ist, keine kompilierte Sprache. Jede Anweisung wird in mehr als eine Maschinensprache übersetzt, und Sie sollten sich nicht um die blutigen Details kümmern.

Menschen, die über die Verwendung --(oder ++) zur effizienten Nutzung von Montageanweisungen sprechen, sind falsch. Diese Anweisungen gelten für Ganzzahlarithmetik und es gibt keine Ganzzahlen in JavaScript, nur Zahlen .

Sie sollten lesbaren Code schreiben.

Salman A.
quelle
3

In vielen Fällen hat dies im Wesentlichen nichts damit zu tun, dass Prozessoren schneller als andere Vergleiche mit Null vergleichen können.

Dies liegt daran, dass nur wenige Javascript-Engines (die in der JIT-Liste) tatsächlich Maschinensprachencode generieren.

Die meisten Javascript-Engines erstellen eine interne Darstellung des Quellcodes, den sie dann interpretieren (um eine Vorstellung davon zu bekommen, wie dies aussieht, werfen Sie einen Blick auf den SpiderMonkey von Firefox am Ende dieser Seite ). Wenn ein Code praktisch dasselbe tut, aber zu einer einfacheren internen Darstellung führt, wird er im Allgemeinen schneller ausgeführt.

Denken Sie daran, dass bei einfachen Aufgaben wie dem Addieren / Subtrahieren einer Variablen zu einer Variablen oder dem Vergleichen einer Variablen mit etwas der Overhead des Interpreters, der von einer internen "Anweisung" zur nächsten wechselt, ziemlich hoch ist, also weniger "Anweisungen" intern von der JS-Engine verwendet, desto besser.

Artelius
quelle
3

Nun, ich weiß nichts über JavaScript, es sollte eigentlich nur eine Frage der Neubewertung der Array-Länge sein und vielleicht etwas mit den assoziativen Arrays zu tun haben (wenn Sie nur dekrementieren, ist es unwahrscheinlich, dass neue Einträge zugewiesen werden müssen - wenn das Array ist dicht, das heißt, jemand kann dafür optimieren).

Bei der Montage auf niedriger Ebene gibt es eine Schleifenanweisung namens DJNZ (Dekrementieren und Springen, wenn nicht Null). Das Dekrementieren und Springen ist also alles in einer Anweisung, was es möglicherweise etwas schneller als INC und JL / JB macht (Inkrementieren, Springen, wenn kleiner als / Springen, wenn unten). Außerdem ist der Vergleich mit Null einfacher als der Vergleich mit einer anderen Zahl. Aber all das ist wirklich marginal und hängt auch von der Zielarchitektur ab (könnte einen Unterschied machen, z. B. bei Arm in einem Smartphone).

Ich würde nicht erwarten, dass diese geringen Unterschiede einen so großen Einfluss auf interpretierte Sprachen haben. Ich habe DJNZ einfach nicht in den Antworten gesehen, also dachte ich, ich würde einen interessanten Gedanken teilen.

das Schwein
quelle
Für die Aufzeichnung DJNZist eine Anweisung in der 8051 (z80) ISA. x86 hat dec/jnzstatt inc/cmp/jneund anscheinend hat arm etwas ähnliches. Ich bin mir ziemlich sicher, dass dies nicht die Ursache für den Unterschied in Javascript ist. Das liegt daran, dass im Loop-Zustand mehr zu bewerten ist.
Peter Cordes
3

Es verwendet zu sagen, dass --i schneller war (in C ++) , weil es nur ein Ergebnis der verringerten Wert. i-- muss den dekrementierten Wert wieder auf i speichern und als Ergebnis auch den ursprünglichen Wert beibehalten (j = i--;). In den meisten Compilern wurden zwei Register anstelle von einem verwendet, was dazu führen könnte, dass eine andere Variable in den Speicher geschrieben werden muss, anstatt als Registervariable beibehalten zu werden.

Ich stimme den anderen zu, die gesagt haben, dass es heutzutage keinen Unterschied macht.

Ameise
quelle
3

In sehr einfachen Worten

"i-- und i ++. Eigentlich dauert es beide die gleiche Zeit".

Aber in diesem Fall, wenn Sie eine inkrementelle Operation haben, wertet der Prozessor die Länge jedes Mal aus, wenn die Variable um 1 erhöht wird, und im Falle einer Dekrementierung. Insbesondere in diesem Fall wird die Länge nur einmal ausgewertet, bis wir 0 erhalten.

Ravi_Parmar
quelle
3

Erstens, i++und i--nehmen Sie sich für jede Programmiersprache, einschließlich JavaScript, genau die gleiche Zeit.

Der folgende Code benötigt eine sehr unterschiedliche Zeit.

Schnell:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

Langsam:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

Daher dauert der folgende Code auch anders.

Schnell:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

Langsam:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

PS Slow ist aufgrund der Optimierung des Compilers nur für einige Sprachen (JavaScript-Engines) langsam. Der beste Weg ist, '<' anstelle von '<=' (oder '=') und '--i' anstelle von 'i--' zu verwenden .

Dmitry
quelle
3

I-- oder i ++ verbrauchen nicht viel Zeit. Wenn Sie tief in die CPU-Architektur eintauchen, ist die ++schneller als die --, da die --Operation das 2er-Komplement ausführt, aber sie geschieht innerhalb der Hardware, so dass sie schneller wird und kein wesentlicher Unterschied zwischen den ++und --auch diesen Operationen berücksichtigt wird am wenigsten Zeit in der CPU verbraucht.

Die for-Schleife läuft folgendermaßen ab:

  • Initialisieren Sie die Variable einmal zu Beginn.
  • Überprüfen Sie die Einschränkung in dem zweiten Operanden der Schleife <, >, <=usw.
  • Wenden Sie dann die Schleife an.
  • Inkrementieren Sie die Schleife und werfen Sie diese Prozesse erneut.

So,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

berechnet die Array-Länge nur einmal zu Beginn und das ist nicht viel Zeit, aber

for(var i = array.length; i--; ) 

berechnet die Länge an jeder Schleife, so dass viel Zeit verbraucht wird.

Omar Freewan
quelle
var i = Things.length - 1; i >= 0; i--berechnet die Länge auch 1 Mal.
Dmitry
1
Ich bin nicht sicher, was " --Operation macht das 2er-Komplement" bedeutet, aber ich gehe davon aus, dass es bedeutet, dass es etwas negiert. Nein, es negiert nichts auf irgendeiner Architektur. Das Subtrahieren von 1 ist genauso einfach wie das Addieren von 1, Sie erstellen einfach eine Schaltung, die eher leiht als trägt.
Olathe
1

Der beste Ansatz zur Beantwortung dieser Art von Fragen besteht darin, sie tatsächlich zu versuchen. Richten Sie eine Schleife ein, die eine Million Iterationen oder was auch immer zählt, und führen Sie sie in beide Richtungen aus. Zeit beide Schleifen und vergleichen Sie die Ergebnisse.

Die Antwort hängt wahrscheinlich davon ab, welchen Browser Sie verwenden. Einige haben andere Ergebnisse als andere.

Greg Hewgill
quelle
4
Das würde seine Frage, warum es schneller ist, immer noch nicht beantworten . Er würde nur einen Benchmark bekommen, ohne zu wissen, warum das so ist ...
Peirix
3
Das ist richtig. Ohne die genaue Implementierung jeder Javascript-Engine in jedem Browser zu kennen, ist es jedoch nahezu unmöglich, den "Warum" -Teil zu beantworten. Viele der Antworten hier werfen anekdotische Empfehlungen wie "Verwenden Sie Vorkürzung anstelle von Nachdekrementierung" (ein C ++ - Optimierungstrick) und "Vergleichen mit Null" auf, was in in nativem Code kompilierten Sprachen zutreffen könnte, aber Javascript ist ziemlich weit vom Bare Metal der Sprache entfernt ZENTRALPROZESSOR.
Greg Hewgill
4
-1 Ich bin völlig anderer Meinung, dass der beste Ansatz darin besteht, es zu versuchen. Ein paar Beispiele ersetzen nicht das kollektive Wissen, und das ist der springende Punkt in Foren wie diesem.
Christophe
1

Ich liebe es, viele Punkte, aber keine Antwort: D.

Einfach ein Vergleich gegen Null ist immer der schnellste Vergleich

(A == 0) gibt also tatsächlich schneller True zurück als (a == 5)

Es ist klein und unbedeutend und mit 100 Millionen Zeilen in einer Sammlung messbar.

dh in einer Schleife nach oben könnten Sie sagen, wo i <= array.length und i inkrementieren

In einer Abwärtsschleife könnten Sie sagen, wo i> = 0 ist, und stattdessen i dekrementieren.

Der Vergleich ist schneller. Nicht die 'Richtung' der Schleife.

Robert
quelle
Es gibt keine Antwort auf die angegebene Frage, da alle Javascript-Engines unterschiedlich sind und die Antwort davon abhängt, in welchem ​​Browser Sie sie messen.
Greg Hewgill
Nein, es sind grundsätzlich akzeptierte Vergleiche gegen Null am schnellsten. Obwohl auch Ihre Aussagen korrekt sind, ist die goldene Regel des Vergleichs mit Null absolut.
Robert
4
Dies gilt nur, wenn der Compiler diese Optimierung vornimmt, was sicherlich nicht garantiert ist. Der Compiler generiert möglicherweise genau den gleichen Code für (a == 0) und (a == 5), mit Ausnahme des Werts der Konstante. Eine CPU wird nicht schneller mit 0 verglichen als mit einem anderen Wert, wenn beide Seiten des Vergleichs Variablen sind (aus Sicht der CPU). Im Allgemeinen haben nur native Code-Compiler die Möglichkeit, auf dieser Ebene zu optimieren.
Greg Hewgill
1

HILFE ANDEREN, EINEN KOPFSCHMERZ ZU VERMEIDEN --- ABSTIMMEN !!!

Die beliebteste Antwort auf dieser Seite funktioniert nicht für Firefox 14 und besteht den jsLinter nicht. "while" -Schleifen benötigen einen Vergleichsoperator, keine Zuordnung. Es funktioniert auf Chrom, Safari und sogar dh. Aber stirbt in Firefox.

DAS IST KAPUTT!

var i = arr.length; //or 10
while(i--)
{
  //...
}

DAS WIRD FUNKTIONIEREN! (funktioniert auf Firefox, übergibt den jsLinter)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}
mrbinky3000
quelle
2
Ich habe es gerade mit Firefox 14 versucht und es hat gut funktioniert. Ich habe Beispiele aus Produktionsbibliotheken gesehen, while (i--)die in vielen Browsern verwendet werden und funktionieren. Könnte an Ihrem Test etwas Seltsames gewesen sein? Haben Sie eine Beta-Version von Firefox 14 verwendet?
Matt Browne
1

Dies ist nur eine Vermutung, aber vielleicht liegt es daran, dass es für den Prozessor einfacher ist, etwas mit 0 (i> = 0) zu vergleichen, als mit einem anderen Wert (i <Things.length).

Rorchackh
quelle
3
+1 Haben andere nicht abgewählt? Obwohl die wiederholte Auswertung der Länge viel mehr ein Problem darstellt als die tatsächliche Erhöhung / Verringerung, kann die Überprüfung des Schleifenendes wichtig sein. Mein C-Compiler gibt einige Warnungen zu Schleifen aus, wie zum Beispiel: "Bemerkung # 1544-D: (ULP 13.1) Erkannter Schleifen-Countdown. Empfehlen Sie, dass Schleifen herunterzählen, da das Erkennen von Nullen einfacher ist"
Harper