Leistung beim Zuweisen von Werten zum Array

Code-Optimierung wird hier in SO gesagt, dass die Profilerstellung der erste Schritt zur Optimierung von Javascript ist und die vorgeschlagenen Engines Profiler von Chrome und Firefox sind. Das Problem bei diesen ist, dass sie auf seltsame Weise die Zeit angeben, zu der jede Funktion ausgeführt wird, aber ich habe kein gutes Verständnis für sie. Am hilfreichsten wäre es, wenn der Profiler angibt, wie oft jede Zeile ausgeführt wird und wenn möglich auch, wie viel Zeit für jede Zeile aufgewendet wird. Auf diese Weise wäre es möglich, die Engpässe genau zu erkennen. Bevor ein solches Tool implementiert / gefunden wird, haben wir zwei Möglichkeiten:

1) Erstelle einen eigenen Taschenrechner, der sowohl die Zeit zählt als auch wie oft ein bestimmter Codeblock oder eine bestimmte Zeile ausgeführt wird. 2) Lerne zu verstehen, welche Methoden langsam sind und welche nicht

Für Option 2jsperf.com ist eine große Hilfe. Ich habe versucht, die Optimierung von Arrays zu lernen und habe einen Geschwindigkeitstest in durchgeführtJSPERF.COM. Das folgende Bild zeigt die Ergebnisse in 5 Hauptbrowsern und zeigt einige Engpässe, die ich vorher nicht kannte.

Die wichtigsten Ergebnisse waren:

1) Das Zuweisen von Werten zu Arrays ist erheblich langsamer als das Zuweisen zu normalen Variablen, unabhängig davon, welche Methode zum Zuweisen verwendet wird.

2) Vorinitialisieren und / oder Vorfüllen des Arrays, bevor leistungskritische Schleifen die Geschwindigkeit erheblich verbessern können

3) Mathematische trigonometrische Funktionen sind nicht so langsam im Vergleich zum Verschieben von Werten in Arrays (!)

Hier sind die Erklärungen zu jedem Test:

1. non_array (100%):

Die Variablen erhielten auf diese Weise einen vordefinierten Wert:

var non_array_0=0;
var non_array_1=0;
var non_array_2=0;
...

und im zeitlichen Bereich hießen sie so:

non_array_0=0;
non_array_1=1;
non_array_2=2;
non_array_3=3;
non_array_4=4;
non_array_5=5;
non_array_6=6;
non_array_7=7;
non_array_8=8;
non_array_9=9;

Das Obige ist eine Array-ähnliche Variable, aber es scheint keine Möglichkeit zu geben, diese Variablen zu iterieren oder auf andere Weise als Gegensatz zum Array zu verweisen. Oder ist da?

Nichts in diesem Test ist schneller als der Variablen eine Nummer zuzuweisen.

2. non_array_non_pre (83,78%)

Genau wie Test 1, aber die Variablen wurden weder vorinitialisiert noch vorbefüllt. Die Geschwindigkeit beträgt 83,78% der Geschwindigkeit von Test 1. In jedem getesteten Browser war die Geschwindigkeit von vorgefüllten Variablen schneller als die von nicht vorgefüllten.Initialisieren Sie also Variablen außerhalb von geschwindigkeitskritischen Schleifen (und füllen Sie sie möglicherweise vor).

Der Testcode ist hier:

var non_array_non_pre_0=0;
var non_array_non_pre_1=0;
var non_array_non_pre_2=0;
var non_array_non_pre_3=0;
var non_array_non_pre_4=0;
var non_array_non_pre_5=0;
var non_array_non_pre_6=0;
var non_array_non_pre_7=0;
var non_array_non_pre_8=0;
var non_array_non_pre_9=0;

3. pre_filled_array (19,96%):

Arrays sind böse! Wenn wir normale Variablen (test1 und test2) wegwerfen und Arrays in das Bild aufnehmen, sinkt die Geschwindigkeit erheblich.Obwohl wir alle Optimierungen vornehmen (Arrays vorinitialisieren und vorab füllen) und dann Werte direkt zuweisen, ohne dass eine Schleife oder ein Push ausgeführt wird, verringert sich die Geschwindigkeit auf 19,96 Prozent. Das ist sehr traurig und ich verstehe wirklich nicht, warum das passiert. Dies war einer der Hauptschocks für mich in diesem Test. Arrays sind so wichtig, und ich habe keinen Weg gefunden, viele Dinge ohne Arrays zu machen.

Die Testdaten sind hier:

pre_filled_array[0]=0;
pre_filled_array[1]=1;
pre_filled_array[2]=2;
pre_filled_array[3]=3;
pre_filled_array[4]=4;
pre_filled_array[5]=5;
pre_filled_array[6]=6;
pre_filled_array[7]=7;
pre_filled_array[8]=8;
pre_filled_array[9]=9;

4. non_pre_filled_array (8,34%):

Dies ist der gleiche Test wie bei 3, aber die Array-Mitglieder sind weder vorinitialisiert noch vorab gefüllt. Die einzige Optimierung bestand darin, das Array vorab zu initialisieren:var non_pre_filled_array=[];

Die Geschwindigkeit sinkt um 58,23% im Vergleich zum vorinitilisierten Test 3.Das Vorinitialisieren und / oder Vorfüllen des Arrays verdoppelt die Geschwindigkeit.

Der Testcode ist hier:

non_pre_filled_array[0]=0;
non_pre_filled_array[1]=1;
non_pre_filled_array[2]=2;
non_pre_filled_array[3]=3;
non_pre_filled_array[4]=4;
non_pre_filled_array[5]=5;
non_pre_filled_array[6]=6;
non_pre_filled_array[7]=7;
non_pre_filled_array[8]=8;
non_pre_filled_array[9]=9;

5. pre_filled_array [i] (7,10%):

Dann zu den Schleifen. Schnellste Loop-Methode in diesem Test. Das Array wurde vorinitialisiert und vorbefüllt.

Der Geschwindigkeitsverlust gegenüber der Inline-Version (Test 3) beträgt 64,44%. Dies ist ein so bemerkenswerter Unterschied, dass ich sagen würde, keine Schleife, wenn nicht benötigt. Wenn das Array klein ist (weiß nicht, wie klein es ist, muss es separat getestet werden), ist es sinnvoller, Inline-Zuweisungen anstelle von Schleifen zu verwenden.

Und weil der Geschwindigkeitsverlust so groß ist und wir wirklich Schleifen brauchen, ist es ratsam, diese zu findenbessere Loop-Methode (z.B.while(i--)).

Der Testcode ist hier:

for(var i=0;i<10;i++)
{
  pre_filled_array[i]=i;
}

6. non_pre_filled_array [i] (5,26%):

Wenn wir das Array nicht vorinitialisieren und vorfüllen, verringert sich die Geschwindigkeit um 25,96%. Auch hier ist das Vorinitialisieren und / oder Vorfüllen vor geschwindigkeitskritischen Schleifen sinnvoll.

Der Code ist hier:

for(var i=0;i<10;i++) 
{
  non_pre_filled_array[i]=i;
}

7. Mathematische Berechnungen (1,17%):

Jeder Test muss ein Bezugspunkt sein. Mathematische Funktionen gelten als langsam. Der Test bestand aus zehn "schweren" mathematischen Berechnungen, aber jetzt kommt das andere, was mich in diesem Test beeindruckt hat. Schauen Sie sich die Geschwindigkeiten 8 und 9 an, bei denen wir zehn ganzzahlige Zahlen in einer Schleife anordnen. Das Berechnen dieser 10 mathematischen Funktionen ist mehr als 30% schneller als das Verschieben von zehn Ganzzahlen in ein Array in einer Schleife. Vielleicht ist es einfacher, einige Array-Pushs in vorinitialisierte Nicht-Arrays zu konvertieren und diese Trigonometrie beizubehalten. Wenn es pro Frame hundert oder tausend Berechnungen gibt, ist es natürlich ratsam, z. sqrt statt sin / cos / tan und benutze taxicab entfernungen für entfernungsvergleiche undDiamantwinkel (t-Bogenmaß) für WinkelvergleicheDer Hauptengpass kann jedoch auch an anderer Stelle liegen: Schleifen sind langsamer als Inlining, Push ist langsamer als die direkte Zuweisung mit Vorinitilisierung und / oder Vorfüllung. Code-Logik, Zeichnungsalgorithmen und DOM-Zugriff können langsam sein. In Javascript kann nicht alles optimiert werden (wir müssen etwas auf dem Bildschirm sehen!), Aber alles, was wir einfach und sinnvoll tun können, ist ratsam. Jemand hier in SO hat gesagt, dass Code für Menschen ist und lesbarer Code wichtiger ist als schneller Code, da die Wartungskosten die größten Kosten sind. Dies ist ein wirtschaftlicher Gesichtspunkt, aber ich habe festgestellt, dass Codeoptimierung beides bieten kann: Eleganz, Lesbarkeit und Leistung. Und wenn eine Leistungssteigerung von 5% erreicht wird und der Code klarer ist, gibt es ein gutes Gefühl!

Der Code ist hier:

non_array_0=Math.sqrt(10435.4557);
non_array_1=Math.atan2(12345,24869);
non_array_2=Math.sin(35.345262356547);
non_array_3=Math.cos(232.43575432);
non_array_4=Math.tan(325);
non_array_5=Math.asin(3459.35498534536);
non_array_6=Math.acos(3452.35);
non_array_7=Math.atan(34.346);
non_array_8=Math.pow(234,222);
non_array_9=9374.34524/342734.255;

8. pre_filled_array.push (i) (0,8%):

Push ist böse! Push zu Loop kombiniert ist böse böse! Dies ist aus irgendeinem Grund eine sehr langsame Methode, um Werte einem Array zuzuweisen. Test 5 (direkte Zuweisungen in einer Schleife) ist fast 9-mal schneller als diese Methode und beide Methoden führen genau das Gleiche aus: Ordnen Sie die Ganzzahl 0-9 einem vorinitialisierten und vorgefüllten Array zu. Ich habe nicht getestet, ob diese Push-for-Loop-Übelkeit auf Pushing oder Looping oder die Kombination von beiden oder die Anzahl der Loops zurückzuführen ist. Es gibt in JSPERF.COM andere Beispiele, die widersprüchliche Ergebnisse liefern. Es ist klüger, nur mit den tatsächlichen Daten zu testen und Entscheidungen zu treffen. Dieser Test ist möglicherweise nicht mit anderen als den verwendeten Daten kompatibel.

Und hier ist der Code:

for(var i=0;i<10;i++)
{
  pre_filled_array.push(i);
}

9. non_pre_filled_array.push (i) (0,74%):

Die letzte und langsamste Methode in diesem Test ist dieselbe wie in Test 8, das Array ist jedoch nicht vorgefüllt. Etwas langsamer als 9, aber der Unterschied ist nicht signifikant (7,23%). Nehmen wir ein Beispiel und vergleichen Sie diese langsamste Methode mit der schnellsten.Die Geschwindigkeit dieser Methode beträgt 0,74% der Geschwindigkeit der Methode 1, was bedeutet, dass Methode 1 135-mal schneller ist als diese. Überlegen Sie sich also genau, ob Arrays in einem bestimmten Anwendungsfall überhaupt benötigt werden. Wenn es sich nur um einen oder wenige Schübe handelt, ist der Geschwindigkeitsunterschied insgesamt nicht erkennbar. Wenn es jedoch nur wenige Schübe gibt, sind sie sehr einfach und elegant in Nicht-Array-Variablen umzuwandeln.

Dies ist der Code:

for(var i=0;i<10;i++)
{
  non_pre_filled_array.push(i);
}

Und zum Schluss die obligatorische SO-Frage:

Gibt es eine Methode, um die Geschwindigkeit von Zuweisungen von Nicht-Array-Variablen und die Dynamik von Arrays zu ermitteln, da der Geschwindigkeitsunterschied nach diesem Test zwischen Zuweisungen von Nicht-Array-Variablen so groß zu sein scheint?

Ich kann nicht benutzenvar variable_$i = 1 in einer Schleife, so dass $ i in eine ganze Zahl konvertiert wird. Ich muss benutzenvar variable[i] = 1 das ist deutlich langsamer alsvar variable1 = 1 wie der Test zeigte. Dies kann nur dann kritisch sein, wenn große Arrays vorhanden sind und dies in vielen Fällen der Fall ist.

EDIT: Ich habe einen neuen Test durchgeführt, um die Langsamkeit des Array-Zugriffs zu bestätigen und habe versucht, einen schnelleren Weg zu finden:

http://jsperf.com/read-write-array-vs-variable

Array-Lese- und / oder Array-Schreibvorgänge sind erheblich langsamer als bei Verwendung normaler Variablen. Wenn einige Vorgänge für Array-Mitglieder ausgeführt werden, ist es ratsam, den Wert des Array-Mitglieds in einer temporären Variablen zu speichern, diese Vorgänge in eine temporäre Variable umzuwandeln und den Wert schließlich im Array-Mitglied zu speichern. Und obwohl der Code größer wird, können diese Vorgänge erheblich schneller inline ausgeführt werden als in einer Schleife.

Fazit: Arrays und normale Variablen sind analog zu Disk und Memory. Normalerweise ist der Speicherzugriff schneller als der Festplattenzugriff und der Zugriff auf normale Variablen ist schneller als der Arrayzugriff. Und Verkettungsvorgänge sind möglicherweise auch schneller als die Verwendung von Zwischenvariablen, aber dies macht Code ein wenig unlesbar.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage