Was sind die Unterschiede zwischen mehrdimensionalen Arrays double[,]
und Array-of-Arrays double[][]
in C #?
Wenn es einen Unterschied gibt, was ist die beste Verwendung für jeden?
Was sind die Unterschiede zwischen mehrdimensionalen Arrays double[,]
und Array-of-Arrays double[][]
in C #?
Wenn es einen Unterschied gibt, was ist die beste Verwendung für jeden?
double[,]
ist ein rechteckiges Array, währenddouble[][]
es als "gezacktes Array" bekannt ist. Die erste hat die gleiche Anzahl von "Spalten" für jede Zeile, während die zweite (möglicherweise) eine andere Anzahl von "Spalten" für jede Zeile hat.Antworten:
Arrays von Arrays (gezackte Arrays) sind schneller als mehrdimensionale Arrays und können effektiver verwendet werden. Mehrdimensionale Arrays haben eine schönere Syntax.
Wenn Sie einfachen Code mit gezackten und mehrdimensionalen Arrays schreiben und dann die kompilierte Assembly mit einem IL-Disassembler untersuchen, werden Sie feststellen, dass das Speichern und Abrufen von gezackten (oder eindimensionalen) Arrays einfache IL-Anweisungen sind, während dieselben Operationen für mehrdimensionale Arrays Methoden sind Aufrufe, die immer langsamer sind.
Betrachten Sie die folgenden Methoden:
Ihre IL wird die folgende sein:
Wenn Sie gezackte Arrays verwenden, können Sie problemlos Vorgänge wie Zeilentausch und Zeilengrößenänderung ausführen. In einigen Fällen ist die Verwendung mehrdimensionaler Arrays möglicherweise sicherer, aber selbst Microsoft FxCop weist darauf hin, dass gezackte Arrays anstelle von mehrdimensionalen Arrays verwendet werden sollten, wenn Sie sie zur Analyse Ihrer Projekte verwenden.
quelle
Ein mehrdimensionales Array erstellt ein schönes lineares Speicherlayout, während ein gezacktes Array mehrere zusätzliche Indirektionsebenen impliziert.
Das Nachschlagen des Werts
jagged[3][6]
in einem gezackten Arrayvar jagged = new int[10][5]
funktioniert folgendermaßen: Suchen Sie das Element bei Index 3 (das ein Array ist) und das Element bei Index 6 in diesem Array (das ein Wert ist). In diesem Fall gibt es für jede Dimension eine zusätzliche Suche (dies ist ein teures Speicherzugriffsmuster).Ein mehrdimensionales Array wird linear im Speicher angeordnet, der tatsächliche Wert wird durch Multiplizieren der Indizes ermittelt. In Anbetracht des Arrays
var mult = new int[10,30]
gibt dieLength
Eigenschaft dieses mehrdimensionalen Arrays jedoch die Gesamtzahl der Elemente zurück, dh 10 * 30 = 300.Die
Rank
Eigenschaft eines gezackten Arrays ist immer 1, aber ein mehrdimensionales Array kann einen beliebigen Rang haben. DieGetLength
Methode eines beliebigen Arrays kann verwendet werden, um die Länge jeder Dimension zu ermitteln. Für das mehrdimensionale Array in diesem Beispiel wirdmult.GetLength(1)
30 zurückgegeben.Die Indizierung des mehrdimensionalen Arrays ist schneller. Wenn Sie beispielsweise das mehrdimensionale Array in diesem Beispiel
mult[1,7]
= 30 * 1 + 7 = 37 haben, erhalten Sie das Element an diesem Index 37. Dies ist ein besseres Speicherzugriffsmuster, da nur ein Speicherort beteiligt ist, nämlich die Basisadresse des Arrays.Ein mehrdimensionales Array weist daher einen kontinuierlichen Speicherblock zu, während ein gezacktes Array nicht quadratisch sein muss, z. B.
jagged[1].Length
nicht gleich sein mussjagged[2].Length
, was für jedes mehrdimensionale Array zutreffen würde.Performance
In Bezug auf die Leistung sollten mehrdimensionale Arrays schneller sein. Viel schneller, aber aufgrund einer wirklich schlechten CLR-Implementierung nicht.
Die erste Zeile enthält Timings von gezackten Arrays, die zweite zeigt mehrdimensionale Arrays und die dritte, so sollte es sein. Das Programm ist unten gezeigt, FYI dies wurde mit Mono getestet. (Die Windows-Timings sind sehr unterschiedlich, hauptsächlich aufgrund der Variationen der CLR-Implementierung.)
Unter Windows sind die Timings der gezackten Arrays weit überlegen, ungefähr so wie meine eigene Interpretation, wie ein mehrdimensionales Array aussehen sollte, siehe 'Single ()'. Leider ist der Windows JIT-Compiler wirklich dumm, und dies macht diese Leistungsdiskussionen leider schwierig, es gibt zu viele Inkonsistenzen.
Dies sind die Timings, die ich für Windows erhalten habe, das gleiche gilt hier, die erste Zeile sind gezackte Arrays, die zweite mehrdimensionale und die dritte meine eigene Implementierung von mehrdimensional. Beachten Sie, wie viel langsamer dies bei Windows im Vergleich zu Mono ist.
Quellcode:
quelle
Einfach ausgedrückt ähneln mehrdimensionale Arrays einer Tabelle in DBMS.
Mit Array of Array (gezacktes Array) kann jedes Element ein anderes Array mit dem gleichen Typ variabler Länge enthalten.
Wenn Sie also sicher sind, dass die Datenstruktur wie eine Tabelle aussieht (feste Zeilen / Spalten), können Sie ein mehrdimensionales Array verwenden. Gezackte Arrays sind feste Elemente und jedes Element kann ein Array variabler Länge enthalten
ZB Pseudocode:
Stellen Sie sich das Obige als 2x2-Tabelle vor:
Stellen Sie sich das Obige als jede Zeile mit variabler Anzahl von Spalten vor:
quelle
Vorwort: Dieser Kommentar soll die Antwort von okutane ansprechen , aber aufgrund des albernen Reputationssystems von SO kann ich ihn nicht dort posten, wo er hingehört .
Ihre Behauptung, dass einer aufgrund der Methodenaufrufe langsamer als der andere ist, ist nicht korrekt. Einer ist langsamer als der andere, weil die Algorithmen zur Überprüfung der Grenzen komplizierter sind. Sie können dies leicht überprüfen, indem Sie nicht die IL, sondern die kompilierte Assembly betrachten. Bei meiner 4.5-Installation sieht der Zugriff auf ein Element (über einen Zeiger in edx), das in einem zweidimensionalen Array gespeichert ist, auf das ecx zeigt, mit in eax und edx gespeicherten Indizes folgendermaßen aus:
Hier sehen Sie, dass Methodenaufrufe keinen Overhead verursachen. Die Überprüfung der Grenzen ist nur sehr kompliziert, da Indizes ungleich Null möglich sind. Diese Funktion wird bei gezackten Arrays nicht angeboten. Wenn wir sub, cmp und jmps für Fälle ungleich Null entfernen, wird der Code so gut wie aufgelöst
(x*y_max+y)*sizeof(ptr)+sizeof(array_header)
. Diese Berechnung ist ungefähr so schnell (eine Multiplikation könnte durch eine Verschiebung ersetzt werden, da dies der ganze Grund ist, warum wir Bytes als Potenzen von zwei Bits auswählen) wie alles andere für den wahlfreien Zugriff auf ein Element.Eine weitere Komplikation besteht darin, dass es viele Fälle gibt, in denen ein moderner Compiler die Prüfung verschachtelter Grenzen auf Elementzugriff optimiert, während er über ein eindimensionales Array iteriert. Das Ergebnis ist Code, der im Grunde nur einen Indexzeiger über den zusammenhängenden Speicher des Arrays bewegt. Die naive Iteration über mehrdimensionale Arrays umfasst im Allgemeinen eine zusätzliche Schicht verschachtelter Logik, sodass ein Compiler die Operation weniger wahrscheinlich optimiert. Obwohl sich der Aufwand für die Überprüfung der Grenzen beim Zugriff auf ein einzelnes Element zu einer konstanten Laufzeit in Bezug auf Array-Dimensionen und -Größen amortisiert, kann die Ausführung eines einfachen Testfalls zum Messen des Unterschieds um ein Vielfaches länger dauern.
quelle
Ich möchte dies aktualisieren, da in .NET Core mehrdimensionale Arrays schneller sind als gezackte Arrays . Ich habe die Tests von John Leidegren ausgeführt. Dies sind die Ergebnisse der .NET Core 2.0-Vorschau 2. Ich habe den Dimensionswert erhöht, um mögliche Einflüsse von Hintergrund-Apps weniger sichtbar zu machen.
Ich habe mich mit Zerlegungen befasst und das habe ich gefunden
jagged[i][j][k] = i * j * k;
brauchte 34 Anweisungen, um auszuführenmulti[i, j, k] = i * j * k;
brauchte 11 Anweisungen, um auszuführensingle[i * dim * dim + j * dim + k] = i * j * k;
brauchte 23 Anweisungen, um auszuführenIch konnte nicht feststellen, warum eindimensionale Arrays immer noch schneller als mehrdimensionale waren, aber ich vermute, dass dies mit einer gewissen Optimierung der CPU zu tun hat
quelle
Mehrdimensionale Arrays sind (n-1) -dimensionale Matrizen.
So
int[,] square = new int[2,2]
ist die quadratische Matrix 2x2,int[,,] cube = new int [3,3,3]
ist eine Würfel - quadratische Matrix 3x3. Verhältnismäßigkeit ist nicht erforderlich.Gezackte Arrays sind nur Arrays von Arrays - ein Array, in dem jede Zelle ein Array enthält.
MDA sind also proportional, JD möglicherweise nicht! Jede Zelle kann ein Array beliebiger Länge enthalten!
quelle
Dies wurde möglicherweise in den obigen Antworten erwähnt, jedoch nicht explizit: Mit einem gezackten Array können Sie
array[row]
eine ganze Datenzeile referenzieren, dies ist jedoch für Multi-D-Arrays nicht zulässig.quelle
Beachten Sie zusätzlich zu den anderen Antworten, dass ein mehrdimensionales Array als ein großes, klobiges Objekt auf dem Heap zugewiesen wird. Dies hat einige Auswirkungen:
<gcAllowVeryLargeObjects>
nach mehrdimensionalen Arrays suchen , bevor das Problem auftritt, wenn Sie nur gezackte Arrays verwenden.quelle
Ich analysiere von ildasm generierte .il-Dateien, um eine Datenbank mit Assemblys, Klassen, Methoden und gespeicherten Prozeduren für eine Konvertierung zu erstellen. Ich bin auf Folgendes gestoßen, was meine Analyse unterbrochen hat.
Das Buch Expert .NET 2.0 IL Assembler von Serge Lidin, Apress, veröffentlicht 2006, Kapitel 8, Primitive Typen und Signaturen, S. 149-150, erklärt.
<type>[]
wird als Vektor von<type>
, bezeichnet<type>[<bounds> [<bounds>**] ]
wird als Array von bezeichnet<type>
**
Mittel können wiederholt werden,[ ]
Mittel optional.Beispiele: Let
<type> = int32
.1)
int32[...,...]
ist eine zweidimensionale Anordnung von undefinierten unteren Grenzen und Größen2)
int32[2...5]
ist eine eindimensionale Anordnung der Untergrenze 2 und der Größe 4.3)
int32[0...,0...]
ist eine zweidimensionale Anordnung von Untergrenzen 0 und undefinierter Größe.Tom
quelle