2009-07-04 5 views
41

Ich habe jetzt an einigen Stellen gelesen, dass die maximale Instanzgröße für eine Struktur 16 Bytes betragen sollte.Warum sollte eine .NET-Struktur weniger als 16 Byte sein?

Aber ich kann nicht sehen, wo diese Nummer (16) herkommt.

Im Internet surfen, habe ich einige gefunden, die vorschlagen, dass es eine ungefähre Zahl für gute Leistung ist, aber Microsoft spricht, als ob es eine harte obere Grenze ist. (z. B. MSDN)

Hat jemand eine endgültige Antwort darüber, warum es 16 Bytes ist?

Antwort

48

Es ist nur eine Leistungsregel der Daumen.

Der Punkt ist, dass, weil Werttypen von Wert übergeben werden, die gesamte Größe der Struktur kopiert werden muss, wenn es an eine Funktion übergeben wird, während für einen Referenztyp nur die Referenz (4 Byte) sein muss kopiert. Eine Struktur kann jedoch ein wenig Zeit sparen, da Sie eine Indirektionsebene entfernen. Selbst wenn sie größer als diese 4 Byte ist, ist sie möglicherweise effizienter als eine Referenz zu übergeben. Aber irgendwann wird es so groß, dass die Kopierkosten spürbar werden. Und eine allgemeine Faustregel ist, dass dies typischerweise um 16 Bytes geschieht. 16 ist gewählt, weil es eine schöne runde Zahl ist, eine Potenz von zwei, und die Alternativen sind entweder 8 (was zu klein ist und Strukturen fast nutzlos machen würde) oder 32 (zu diesem Zeitpunkt sind die Kosten für das Kopieren der Struktur bereits problematisch Wenn Sie aus Gründen der Leistung Strukturen verwenden)

Aber letztlich ist dies eine Leistungsempfehlung. Es beantwortet die Frage "Welches wäre am effizientesten zu verwenden? Eine Struktur oder eine Klasse?". Aber es beantwortet nicht die Frage, "welche Karte am besten zu meiner Problemdomäne passt".

Strukturen und Klassen verhalten sich unterschiedlich. Wenn Sie das Verhalten einer Struktur benötigen, würde ich sagen, dass Sie es zu einer Struktur machen, unabhängig von der Größe. Zumindest bis Sie Leistungsprobleme haben, profilieren Sie Ihren Code und stellen Sie fest, dass Ihre Struktur ein Problem darstellt.

Ihr Link sagt auch, dass es nur eine Frage der Leistung ist:

Wenn ein oder mehr diese Bedingungen nicht erfüllt, erstellen Sie einen Referenztyp statt eine Struktur. Fehler bei Einhaltung dieser Richtlinie kann die Leistung negativ beeinflussen.

+1

Ja, der Link sagt, es ist eine Frage der Leistung, aber ist auch ziemlich stark in der Sprache, die es verwendet, d. H. "Definieren Sie keine Struktur ...". Sie hätten sagen können: "Es ist nicht ratsam ..." – Joe

+0

Stimmt, die Formulierung scheint ein bisschen stark zu sein. Aber es könnte sein, zu betonen, dass Heap-allokierte Klassen nicht langsam sind (wie Programmierer aus C/C++ erwarten könnten) – jalf

+4

Eine mögliche Antwort für die genaue Anzahl ist, dass eine 16-Byte-Struktur immer noch klein genug ist, um auf die CPUs zu passen Speicherbus, oder als Teil eines SIMD-Befehls kopiert werden. Größere Strukturen werden komplexer zum Kopieren oder Lesen/Schreiben. – jalf

0

Ich denke, die 16 Bytes ist nur eine Faustregel aus Sicht der Leistung. Ein Objekt in .NET verwendet mindestens 24 Byte Arbeitsspeicher (IIRC). Wenn Sie Ihre Struktur also viel größer machen, ist ein Referenztyp vorzuziehen.

Ich kann mir keinen Grund vorstellen, warum sie 16 Bytes speziell gewählt haben.

+0

* Ein Objekt in .NET verwendet mindestens 24 Byte Speicher (IIRC) *. Haben Sie eine Referenz? – nawfal

21

Die Größe ergibt sich größtenteils aus der Zeit, die benötigt wird, um die Struktur auf dem Stapel zu kopieren, um beispielsweise an eine Methode zu übergeben. Alles viel größer als das und Sie verbrauchen viel Speicherplatz und CPU-Zyklen nur Daten kopieren - wenn eine Referenz auf eine unveränderliche Klasse (sogar mit Dereferenzierung) könnte viel effizienter sein.

22

Wenn eine Struktur nicht größer als 16 Byte ist, kann sie mit ein paar einfachen Prozessoranweisungen kopiert werden. Wenn es größer ist, wird eine Schleife zum Kopieren der Struktur verwendet.

Solange die Struktur nicht größer als 16 Bytes ist, muss der Prozessor beim Kopieren der Struktur ungefähr so ​​arbeiten wie beim Kopieren einer Referenz.Wenn die Struktur größer ist, verlieren Sie den Leistungsvorteil der s-Struktur, und Sie sollten es in der Regel stattdessen zu einer Klasse machen.

+1

Ich bin kein x86 Assembly Guru, verdammt, ich bin wahrscheinlich ein komplettes N00b in der Tat - aber ich bin wirklich neugierig, könnten Sie Ihre Antwort mit einem Beispielcode aktualisieren, um dies zu zeigen? Ist es wichtig, ob der Prozessor im 32-Bit- oder im 64-Bit-Modus läuft? – Goyuix

+1

@Goyuix: Es wird natürlich einige Leistungsunterschiede zwischen 32-Bit- und 64-Bit-Code geben, aber es folgt den gleichen Prinzipien. Ich habe vor einiger Zeit einen Leistungstest gemacht.Der Code besteht nur aus vielen Structs und vielen Loops, also ist das nicht so interessant, aber Sie können das Ergebnis hier sehen: http://StackOverflow.com/Questions/2437925/Why-is-struct-Better-with- Sein-weniger-als-16-Bytes/2437938 # 2437938 – Guffa

+1

Es scheint mir, als ob dies die richtige Antwort ist, und ich bin nicht sicher, warum es meistens übersehen wurde. Diese Perf-Vergleichstabelle auf Ihrer anderen Antwort ist sehr aussagekräftig. –

2

Hier ist ein Szenario, in dem structs überragende Leistung aufweisen kann:

Wenn Sie 1000s Instanzen erstellen müssen. In diesem Fall müssten Sie, wenn Sie eine Klasse verwenden, zuerst das Array zuweisen, das die 1000er Instanzen enthält, und dann jede Instanz in einer Schleife zuweisen. Wenn Sie jedoch Strukturen verwenden, werden die 1000er Instanzen sofort verfügbar, nachdem Sie das Array zugewiesen haben, das sie enthalten soll.

Darüber hinaus sind Strukturen sehr nützlich, wenn Sie Interop ausführen müssen oder aus Sicherheitsgründen in unsicheren Code eintauchen möchten.

Wie immer gibt es einen Kompromiss und man muss analysieren, was sie tun, um den besten Weg zu finden, etwas zu implementieren.

ps: Dieses Szenario kam ins Spiel, als ich mit LIDAR-Daten arbeitete, bei denen Millionen von Punkten x, y, z und andere Attribute für Bodendaten darstellen konnten. Diese Daten mussten für eine intensive Berechnung in den Speicher geladen werden, um alle Arten von Daten auszugeben.

7

Wie andere Antworten angemerkt haben, sind die Kosten pro Byte beim Kopieren einer Struktur, die größer als ein bestimmter Schwellenwert ist (in früheren Versionen von .NET 16 Byte, inzwischen aber auf 20-24 angewachsen) wesentlich größer als die pro-byte-Kosten einer kleineren Struktur. Es ist jedoch wichtig zu beachten, dass das Kopieren einer Struktur beliebig einer bestimmten Größe einmal ein Bruchteil der Kosten sein wird, die eine neue Klassenobjektinstanz derselben Größe erzeugen. Wenn eine Struktur während ihrer Lebensdauer viele Male kopiert wird und die Semantik vom Werttyp nicht besonders benötigt wird, kann ein Klassenobjekt vorzuziehen sein. Wenn jedoch eine Struktur nur einmal oder zweimal kopiert würde, wäre ein solches Kopieren wahrscheinlich billiger als die Erstellung eines neuen Klassenobjekts. Die Break-Even-Anzahl von Kopien, bei denen ein Klassenobjekt billiger wird, variiert mit der Größe der fraglichen Struktur/des fraglichen Objekts, ist aber für Dinge, die unterhalb des "billigen Kopier" -Schwellenwerts liegen, viel höher als für die obigen Dinge.

BTW, ein weiterer erwähnenswerter Punkt ist, dass die Kosten für die Übergabe einer Struktur als ref Parameter unabhängig von der Größe der Struktur ist. In vielen Fällen kann eine optimale Leistung erreicht werden, indem Werttypen verwendet und an ref übergeben werden. Es muss jedoch darauf geachtet werden, dass die Verwendung von Eigenschaften oder readonly Feldern von Strukturtypen vermieden wird, da der Zugriff auf eine dieser Strukturen eine implizite temporäre Kopie der betreffenden Struktur erzeugt.

+1

Ich kannte diesen Teil nicht über die Verwendung von Eigenschaften oder 'readonly' Feldern. Kannst du mich auf einen Link zum Weiterlesen bringen? –

+1

@Justin: Ein Eigenschaften-Getter ist nichts anderes als eine Methode, deren Rückgabetyp der Typ der betreffenden Eigenschaft ist. Daher muss die Eigenschaft die Informationen in alle Register oder Speicherorte kopieren, die für den Rückgabewert verwendet werden. Schreibgeschützte Felder werden beim Zugriff auf beliebige Eigenschaften oder Methoden kopiert, da der Compiler nicht wissen kann, ob die aufgerufene Methode versuchen könnte, die Struktur zu ändern, auf der sie aufgerufen wird. Anders als in C# hat .net keinen Mechanismus, um zu verhindern, dass eine struct-Methode die an sie übergebene Struktur ändert; Es gab zwei Möglichkeiten, wie Microsoft mit dieser Situation umgehen konnte: – supercat

+1

(1) Erlaube Read-Only-Strukturen, direkt an Methoden übergeben zu werden, und hoffe, dass niemand versucht, sie an Mutationsmethoden weiterzugeben, oder (2) eine temporäre Kopie von a schreibgeschützte Struktur und übergibt diese Kopie an die aufgerufene Methode. Beachten Sie, dass (2) langsamer als (1) ist und im Allgemeinen kein korrektes Verhalten in Fällen liefert, in denen (1) dies auch nicht wäre; Was (2) tut, ist die Art des falschen Verhaltens zu ändern, wenn ein Versuch unternommen wird, eine schreibgeschützte Struktur zu ändern. – supercat

Verwandte Themen