Als @dlev erwähnt, ist dies aufgrund locality of reference und hat damit zu tun, wie die physische Hardware im Computer funktioniert.
Im Computer gibt es viele verschiedene Arten von Speicher. Typischerweise können nur bestimmte Speicherstellen (Register) tatsächliche Operationen an ihnen ausführen; den Rest der Zeit, wenn Sie Operationen auf Daten durchführen, müssen Sie es aus dem Speicher in ein Register laden, einige Berechnungen durchführen, dann schreiben Sie es zurück.
Hauptspeicher (RAM) ist viel, viel langsamer als Register, oft um einen Faktor von Hunderten oder Tausenden. Folglich sollte das Lesen aus dem Speicher möglichst vermieden werden. Um dies zu beheben, verfügen die meisten Computer in der Regel über spezielle Speicherbereiche mit der Bezeichnung caches. Die Aufgabe des Cachespeichers besteht darin, Daten aufzubewahren, auf die kürzlich aus dem Speicher zugegriffen wurde, so dass, wenn auf denselben Speicherbereich erneut zugegriffen wird, der Wert aus dem Cache (schnell) anstatt aus dem Hauptspeicher (langsam) gezogen werden kann. Normalerweise werden Caches so entworfen, dass, wenn ein Wert aus dem Speicher eingelesen wird, dieser Wert plus eine ganze Reihe benachbarter Werte in den Cache gezogen wird. Wenn Sie also über ein Array iterieren, werden nach dem Lesen des ersten Werts die restlichen Werte aus dem Array im Cache gespeichert und können effizienter abgerufen werden.
Der Grund dafür, dass Ihr Code langsamer ist, als er sein muss, besteht darin, dass er nicht sequentiell auf die Array-Elemente zugreift. In C sind 2D-Arrays in row-major order angelegt, was bedeutet, dass der Speicher als
A[0][0] A[0][4] A[0][5] ... A[1][0] A[1][6] A[1][7] ... A[2][0] A[2][8] A[2][9] ...
Folglich angeordnet ist, wenn Sie diese für Schleife verwenden:
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
// Do something with A[i][j]
}
}
Dazu ausgezeichnete Lokalität bekommen, da werden Sie Zugriff auf Array-Elemente in der Reihenfolge, in der sie im Speicher erscheinen. Dies macht die Anzahl der Lesevorgänge des Hauptspeichers sehr klein, da sich alles typischerweise im Cache befindet und betriebsbereit ist.
Allerdings, wenn Sie die Schleifen austauschen, wie Sie getan haben, springen Ihre Zugriffe im Speicher und sind nicht unbedingt aufeinander folgend. Dies bedeutet, dass Sie viele Cache-Fehler haben, in denen die Speicheradresse, die Sie als nächstes lesen, nicht im Cache ist. Dies erhöht die Anzahl der Cache-Ladevorgänge, was das Programm dramatisch verlangsamen kann.
Compiler werden immer intelligenter, um solche Schleifen automatisch auszutauschen, aber wir sind immer noch weit davon entfernt, diese Details ignorieren zu können. Wenn Sie C- oder C++ - Code für mehrdimensionale Arrays schreiben, sollten Sie in der Regel versuchen, in der Reihenfolge der Zeilenreihenfolge und nicht der Spaltenreihenfolge zu iterieren. Sie können bemerkenswerte Beschleunigungen in Ihrem Programm bekommen.
Hoffe, das hilft!
Ort der Referenz: Sie sind unnötig CPU-Cache auf die "langsame" Weise ungültig. – dlev
@dlev: Warum postest du das nicht als Antwort? –
weil dlev nicht über die rep ist. dlev ist über die Liebe – Robotnik