webentwicklung-frage-antwort-db.com.de

For-Loop-Performance: Speichern der Array-Länge in einer Variablen

Betrachten Sie zwei Versionen derselben Schleifeniteration:

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

und

var len = nodes.length;
for (var i = 0; i < len; i++) {
    ...
}

Ist die letztere Version irgendwie schneller als die vorherige?

47
ducin

Aktualisierung: 16.12.2015

Da diese Antwort immer noch viele Ansichten zu bekommen scheint, wollte ich das Problem erneut untersuchen, während sich Browser und JS-Engines weiterentwickeln.

Anstatt JSPerf zu verwenden, habe ich etwas Code zusammengestellt, um Arrays mit beiden in der ursprünglichen Frage genannten Methoden zu durchlaufen. Ich habe den Code in Funktionen unterteilt, um die Funktionalität so aufzuschlüsseln, wie es hoffentlich in einer realen Anwendung möglich wäre:

function getTestArray(numEntries) {
    var testArray = [];
    for(var i = 0; i < numEntries; i++) {
        testArray.Push(Math.random());
    }
    return testArray;
}

function testInVariable(testArray) {
    for (var i = 0; i < testArray.length; i++) {
        doSomethingAwesome(testArray[i]);
    }
}

function testInLoop(testArray) {
    var len = testArray.length;
    for (var i = 0; i < len; i++) {
        doSomethingAwesome(testArray[i]);
    }
}

function doSomethingAwesome(i) {
    return i + 2;
}

function runAndAverageTest(testToRun, testArray, numTimesToRun) {
    var totalTime = 0;
    for(var i = 0; i < numTimesToRun; i++) {
        var start = new Date();
        testToRun(testArray);
        var end = new Date();
        totalTime += (end - start);
    }
    return totalTime / numTimesToRun;
}

function runTests() {
    var smallTestArray = getTestArray(10000);
    var largeTestArray = getTestArray(10000000);

    var smallTestInLoop = runAndAverageTest(testInLoop, smallTestArray, 5);
    var largeTestInLoop = runAndAverageTest(testInLoop, largeTestArray, 5);
    var smallTestVariable = runAndAverageTest(testInVariable, smallTestArray, 5);
    var largeTestVariable = runAndAverageTest(testInVariable, largeTestArray, 5);

    console.log("Length in for statement (small array): " + smallTestInLoop + "ms");
    console.log("Length in for statement (large array): " + largeTestInLoop + "ms");
    console.log("Length in variable (small array): " + smallTestVariable + "ms");
    console.log("Length in variable (large array): " + largeTestVariable + "ms");
}

runTests();
runTests();
runTests();

Um einen möglichst fairen Test zu erzielen, wird jeder Test fünfmal durchgeführt und die Ergebnisse gemittelt. Ich habe auch den gesamten Test einschließlich der Erzeugung des Arrays dreimal ausgeführt. Tests mit Chrome auf meinem Computer ergaben, dass die Zeit, die mit den einzelnen Methoden benötigt wurde, nahezu identisch war.

Es ist wichtig, sich daran zu erinnern, dass dieses Beispiel ein Spielzeugbeispiel ist. Tatsächlich liefern die meisten Beispiele, die aus dem Kontext Ihrer Anwendung entnommen wurden, wahrscheinlich unzuverlässige Informationen, da die anderen Dinge, die Ihr Code ausführt, die Leistung direkt oder indirekt beeinflussen können.

Die unterste Zeile

Der beste Weg, um festzustellen, welche Leistung für Ihre Anwendung am besten ist, besteht darin, sie selbst zu testen! JS-Engines, Browsertechnologie und CPU-Technologie werden ständig weiterentwickelt. Daher ist es unerlässlich, dass Sie die Leistung im Kontext Ihrer Anwendung immer selbst testen. Es lohnt sich auch, sich zu fragen, ob Sie überhaupt ein Leistungsproblem haben. Wenn Sie dann keine Zeit damit verbringen, für den Benutzer nicht wahrnehmbare Mikrooptimierungen vorzunehmen, können Sie Fehler besser beheben und Funktionen hinzufügen, was zu glücklicheren Benutzern führt :).

Ursprüngliche Antwort:

Letzteres wäre etwas schneller. Die length -Eigenschaft durchläuft das Array nicht, um die Anzahl der Elemente zu überprüfen, aber jedes Mal, wenn es für das Array aufgerufen wird, muss das Array dereferenziert werden. Durch Speichern der Länge in einer Variablen ist die Array-Dereferenzierung nicht bei jeder Iteration der Schleife erforderlich.

Wenn Sie an der Leistung verschiedener Methoden zum Durchlaufen eines Arrays in Javascript interessiert sind, schauen Sie sich diese an jsperf

27
Neil Mountford

Die akzeptierte Antwort ist nicht richtig, da eine anständige Engine in der Lage sein sollte, die Eigenschaftslast aus der Schleife zu heben mit so einfachen Schleifenkörpern. 

Siehe this jsperf - zumindest in V8 es ist interessant zu sehen , wie das Speichern in einer Variablen die Registerzuordnung ändert - im Code, in dem die Variable verwendet wird, wird die Variable sum gespeichert der Stack, während er mit dem array.length- in-a-loop-Code in einem Register gespeichert wird. Ich gehe davon aus, dass in SpiderMonkey und Opera etwas Ähnliches passiert.

Laut dem Autor ist JSPerf wird falsch verwendet , 70% der Zeit. Diese gebrochenen Jsperfs, wie sie in allen Antworten hier angegeben sind, führen zu irreführenden Ergebnissen und die Leute ziehen daraus falsche Schlüsse.

Einige rote Flaggen setzen Code in die Testfälle anstelle von Funktionen. Sie testen das Ergebnis nicht auf Korrektheit oder verwenden einen Mechanismus zum Beseitigen toter Code-Eliminierung, Definieren von Funktionen in Setup- oder Testfällen anstelle von Global. Für die Konsistenz möchten Sie Sie können die Testfunktionen auch vor jedem Benchmark einrichten, damit das Kompilieren nicht im Zeitabschnitt erfolgt.

33
Esailija

Gemäß w3schools "Aktivität in Schleifen reduzieren" gilt Folgendes als fehlerhafter Code:

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

Und folgendes gilt als guter Code:

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

Da der Zugriff auf das DOM langsam ist, wurde Folgendes geschrieben, um die Theorie zu testen:

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>my test scripts</title>
	</head>
	
	<body>
		<button onclick="initArray()">Init Large Array</button>
		<button onclick="iterateArraySlowly()">Iterate Large Array Slowly</button>
		<button onclick="iterateArrayQuickly()">Iterate Large Array Quickly</button>
		
		<p id="slow">Slow Time: </p>
		<p id="fast">Fast Time: </p>
		<p id="access"></p>


	
	<script>
	var myArray = [];
			
		function initArray(){
			var length = 1e6;
			var i;
			for(i = 0; i < length; i++) {
				myArray[i] = i;
			}
			console.log("array size: " + myArray.length);
		}
		
		function iterateArraySlowly() {
			var t0 = new Date().getTime();
			var slowText = "Slow Time: "
			var i, t;
			var Elm = document.getElementById("slow");
			for (i = 0; i < myArray.length; i++) {
				document.getElementById("access").innerHTML = "Value: " + i;
			}
			t = new Date().getTime() - t0;
			Elm.innerHTML = slowText + t + "ms";
		}
		
		function iterateArrayQuickly() {
			var t0 = new Date().getTime();
			var fastText = "Fast Time: "
			var i, t;
			var Elm = document.getElementById("fast");
			var length = myArray.length;
			for (i = 0; i < length; i++) {
				document.getElementById("access").innerHTML = "Value: " + i;
			}
			t = new Date().getTime() - t0;
			Elm.innerHTML = fastText + t + "ms";
		
		}
	</script>
	</body>
</html>

Das Interessante ist, dass die zuerst durchgeführte Iteration immer über den anderen zu gewinnen scheint. Aber was als "schlechter Code" gilt, scheint die Mehrheit der Zeit zu gewinnen, nachdem er einige Male ausgeführt wurde. Vielleicht kann jemand, der klüger ist als ich, den Grund dafür erklären. Aber fürs Erste halte ich mich an das, was für mich lesbarer ist:

for (i = 0; i < arr.length; i++) {
4
Tyler B. Long

wenn nodesDOM nodeList ist, wird die zweite Schleife viel schneller sein, da Sie in der ersten Schleife DOM (sehr teuer) bei jeder Wiederholung nachschlagen. jsperf

2
Molecular Man

Ich glaube, dass der nodes.length bereits definiert ist und nicht bei jeder Verwendung neu berechnet wird. Das erste Beispiel wäre also schneller, weil es eine Variable weniger definiert hat. Der Unterschied wäre jedoch nicht zu bemerken.

1
Grallen

Dies war schon immer der leistungsfähigste Benchmark-Test, den ich je verwendet habe.

for (i = 0, val; val = nodes[i]; i++) {
    doSomethingAwesome(val);
}
0