Ich bin dabei, 100.000 Objekte im Code zu erstellen. Sie sind klein, nur mit 2 oder 3 Eigenschaften. Ich werde sie in eine generische Liste aufnehmen und wenn sie es sind, werde ich sie schleifen und den Wert überprüfen a
und möglicherweise den Wert aktualisieren b
.
Ist es schneller / besser, diese Objekte als Klasse oder als Struktur zu erstellen?
BEARBEITEN
ein. Die Eigenschaften sind Werttypen (außer der Zeichenfolge, denke ich?)
b. Sie könnten (wir sind uns noch nicht sicher) eine Validierungsmethode haben
BEARBEITEN 2
Ich habe mich gefragt: Werden Objekte auf dem Heap und dem Stapel vom Garbage Collector gleichermaßen verarbeitet, oder funktioniert das anders?
Antworten:
Sie sind die einzige Person, die die Antwort auf diese Frage bestimmen kann. Probieren Sie es in beide Richtungen aus, messen Sie eine aussagekräftige, benutzerorientierte, relevante Leistungsmetrik, und Sie werden wissen, ob die Änderung in relevanten Szenarien eine sinnvolle Auswirkung auf echte Benutzer hat.
Strukturen verbrauchen weniger Heapspeicher (weil sie kleiner und leichter zu komprimieren sind, nicht weil sie "auf dem Stapel" sind). Das Kopieren dauert jedoch länger als das Referenzkopieren. Ich weiß nicht, wie hoch Ihre Leistungsmetriken für die Speichernutzung oder die Geschwindigkeit sind. Hier gibt es einen Kompromiss und Sie sind die Person, die weiß, was es ist.
Vielleicht Klasse, vielleicht Struktur. Als Faustregel gilt: Wenn das Objekt ist:
1. Klein
2. Logischerweise ein unveränderlicher Wert
3. Es gibt viele davon
Dann würde ich in Betracht ziehen, es zu einer Struktur zu machen. Ansonsten würde ich mich an einen Referenztyp halten.
Wenn Sie ein Feld einer Struktur mutieren müssen, ist es normalerweise besser, einen Konstruktor zu erstellen, der eine vollständig neue Struktur mit dem korrekt festgelegten Feld zurückgibt. Das ist vielleicht etwas langsamer (messen Sie es!), Aber logischerweise viel einfacher zu überlegen.
Nein , sie sind nicht identisch, da Objekte auf dem Stapel die Wurzeln der Sammlung sind . Der Garbage Collector muss nie fragen: "Lebt das Ding auf dem Stapel?" weil die Antwort auf diese Frage immer "Ja, es ist auf dem Stapel" lautet. (Jetzt können Sie sich nicht darauf verlassen, dass ein Objekt am Leben bleibt, da der Stapel ein Implementierungsdetail ist. Der Jitter darf Optimierungen einführen, die beispielsweise den normalerweise als Stapelwert registrierten Wert registrieren, und dann ist er nie auf dem Stapel Der GC weiß also nicht, dass er noch lebt. Bei einem registrierten Objekt können seine Nachkommen aggressiv gesammelt werden, sobald das Register, das sich daran festhält, nicht erneut gelesen wird.)
Der Garbage Collector muss jedoch Objekte auf dem Stapel als lebendig behandeln, genauso wie er jedes Objekt, von dem bekannt ist, dass es lebt, als lebendig behandelt. Das Objekt auf dem Stapel kann sich auf Heap-zugewiesene Objekte beziehen, die am Leben erhalten werden müssen. Daher muss der GC Stapelobjekte wie lebende Heap-zugewiesene Objekte behandeln, um die Live-Menge zu bestimmen. Aber offensichtlich werden sie nicht als "lebende Objekte" behandelt, um den Heap zu komprimieren, da sie sich überhaupt nicht auf dem Heap befinden.
Ist das klar?
quelle
readonly
), um Optimierungen zu ermöglichen. Ich würde nicht zulassen, dass sich dies auf die Wahl der Veränderlichkeit auswirkt (ich bin theoretisch eine Nuss für Effizienzdetails, aber in der Praxis besteht mein erster Schritt in Richtung Effizienz immer darin, eine möglichst einfache Garantie für die Korrektheit zu haben, die ich kann und daher nicht muss Verschwenden Sie CPU-Zyklen und Gehirnzyklen bei Überprüfungen und Randfällen, und es hilft dort, angemessen veränderlich oder unveränderlich zu sein. Dies würde jedoch jeder Reaktion auf Ihre Aussage entgegenwirken, dass Unveränderlichkeit langsamer sein kann.Manchmal müssen
struct
Sie den new () -Konstruktor nicht aufrufen und die Felder direkt zuweisen, wodurch er viel schneller als gewöhnlich wird.Beispiel:
ist etwa 2 bis 3 mal schneller als
wo
Value
ist einstruct
mit zwei Feldern (id
undisValid
).Auf der anderen Seite müssen die Elemente verschoben oder ausgewählte Werttypen ausgewählt werden, was Sie durch das Kopieren verlangsamen wird. Um die genaue Antwort zu erhalten, müssen Sie vermutlich Ihren Code profilieren und testen.
quelle
list
, da der angegebene Code nicht mit a funktioniertList<Value>
.Strukturen scheinen Klassen ähnlich zu sein, aber es gibt wichtige Unterschiede, die Sie beachten sollten. Zuallererst sind Klassen Referenztypen und Strukturen Werttypen. Mithilfe von Strukturen können Sie Objekte erstellen, die sich wie die integrierten Typen verhalten und deren Vorteile ebenfalls nutzen.
Wenn Sie den Operator New für eine Klasse aufrufen, wird er auf dem Heap zugewiesen. Wenn Sie jedoch eine Struktur instanziieren, wird sie auf dem Stapel erstellt. Dies führt zu Leistungssteigerungen. Außerdem werden Sie nicht wie bei Klassen mit Verweisen auf eine Instanz einer Struktur umgehen. Sie arbeiten direkt mit der struct-Instanz. Wenn Sie eine Struktur an eine Methode übergeben, wird sie daher nicht als Referenz, sondern als Wert übergeben.
Mehr hier:
http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx
quelle
Arrays von Strukturen werden auf dem Heap in einem zusammenhängenden Speicherblock dargestellt, während ein Array von Objekten als zusammenhängender Referenzblock mit den tatsächlichen Objekten selbst an anderer Stelle auf dem Heap dargestellt wird, wodurch Speicher sowohl für die Objekte als auch für ihre Array-Referenzen erforderlich ist .
In diesem Fall wäre es effizienter, Strukturen zu verwenden , wenn Sie sie in a platzieren
List<>
(und aList<>
wird auf ein Array gesichert).(Beachten Sie jedoch, dass große Arrays ihren Weg auf dem Heap für große Objekte finden, wo sich bei langer Lebensdauer die Speicherverwaltung Ihres Prozesses nachteilig auswirken kann. Denken Sie auch daran, dass der Speicher nicht die einzige Überlegung ist.)
quelle
ref
Schlüsselwort verwenden, um damit umzugehen.Wenn sie eine Wertesemantik haben, sollten Sie wahrscheinlich eine Struktur verwenden. Wenn sie Referenzsemantik haben, sollten Sie wahrscheinlich eine Klasse verwenden. Es gibt Ausnahmen, die meistens dazu neigen, eine Klasse zu erstellen, selbst wenn es eine Wertsemantik gibt, aber von dort aus beginnen.
Bei Ihrer zweiten Bearbeitung befasst sich der GC nur mit dem Heap, aber es gibt viel mehr Heap-Speicherplatz als Stapelspeicher, sodass das Platzieren von Dingen auf dem Stapel nicht immer ein Gewinn ist. Außerdem befinden sich in beiden Fällen eine Liste der Strukturtypen und eine Liste der Klassentypen auf dem Heap, sodass dies in diesem Fall irrelevant ist.
Bearbeiten:
Ich fange an, den Begriff böse als schädlich zu betrachten. Schließlich ist es eine schlechte Idee, eine Klasse veränderbar zu machen, wenn sie nicht aktiv benötigt wird, und ich würde nicht ausschließen, jemals eine veränderbare Struktur zu verwenden. Es ist eine schlechte Idee, so oft, dass es fast immer eine schlechte Idee ist, aber meistens stimmt sie einfach nicht mit der Wertesemantik überein, so dass es im gegebenen Fall einfach keinen Sinn macht, eine Struktur zu verwenden.
Bei privaten verschachtelten Strukturen kann es vernünftige Ausnahmen geben, bei denen alle Verwendungen dieser Struktur daher auf einen sehr begrenzten Bereich beschränkt sind. Dies gilt hier jedoch nicht.
Wirklich, ich denke, "es mutiert, also ist es ein schlechtes Produkt" ist nicht viel besser, als über den Heap und den Stack zu sprechen (was zumindest einige Auswirkungen auf die Leistung hat, selbst wenn es sich um ein häufig falsch dargestelltes handelt). "Es mutiert, daher ist es sehr wahrscheinlich nicht sinnvoll, es als Wertsemantik zu betrachten, also ist es eine schlechte Struktur" ist nur geringfügig anders, aber wichtig, denke ich.
quelle
Die beste Lösung besteht darin, zu messen, erneut zu messen und dann noch etwas zu messen. Möglicherweise gibt es Details zu Ihrer Arbeit, die eine vereinfachte und einfache Antwort wie "Strukturen verwenden" oder "Klassen verwenden" erschweren.
quelle
Eine Struktur ist im Kern nichts anderes als eine Ansammlung von Feldern. In .NET kann eine Struktur "vorgeben", ein Objekt zu sein, und für jeden Strukturtyp definiert .NET implizit einen Heap-Objekttyp mit denselben Feldern und Methoden, die sich als Heap-Objekt wie ein Objekt verhalten . Eine Variable, die einen Verweis auf ein solches Heap-Objekt enthält ("Boxed" -Struktur), weist eine Referenzsemantik auf, aber eine Variable, die eine Struktur direkt enthält, ist einfach eine Aggregation von Variablen.
Ich denke, ein Großteil der Verwirrung zwischen Struktur und Klasse beruht auf der Tatsache, dass Strukturen zwei sehr unterschiedliche Anwendungsfälle haben, die sehr unterschiedliche Entwurfsrichtlinien haben sollten, aber die MS-Richtlinien unterscheiden nicht zwischen ihnen. Manchmal besteht Bedarf an etwas, das sich wie ein Objekt verhält. In diesem Fall sind die MS-Richtlinien ziemlich vernünftig, obwohl das "16-Byte-Limit" wahrscheinlich eher 24-32 betragen sollte. Manchmal ist jedoch eine Aggregation von Variablen erforderlich. Eine zu diesem Zweck verwendete Struktur sollte einfach aus einer Reihe öffentlicher Felder und möglicherweise aus einem
Equals
Override,ToString
Override und bestehenIEquatable(itsType).Equals
Implementierung. Strukturen, die als Aggregationen von Feldern verwendet werden, sind keine Objekte und sollten dies auch nicht vorgeben. Aus Sicht der Struktur sollte die Bedeutung des Feldes nicht mehr oder weniger sein als "das Letzte, was in dieses Feld geschrieben wurde". Jede zusätzliche Bedeutung sollte durch den Client-Code bestimmt werden.Wenn beispielsweise eine variabel aggregierende Struktur Mitglieder hat
Minimum
undMaximum
, sollte die Struktur selbst dies nicht versprechenMinimum <= Maximum
. Code, der eine solche Struktur als Parameter empfängt, sollte sich so verhalten, als ob er separatMinimum
und mitMaximum
Werten übergeben würde. Eine Anforderung,Minimum
die nicht größer alsMaximum
ist, sollte als Anforderung angesehen werden, dass einMinimum
Parameter nicht größer als ein separat übergebener Parameter sein darfMaximum
.Ein nützliches Muster, das manchmal berücksichtigt werden muss, ist die
ExposedHolder<T>
Definition einer Klasse wie:Wenn man eine Struktur hat
List<ExposedHolder<someStruct>>
, bei dersomeStruct
es sich um eine variabel aggregierende Struktur handelt, kann man Dinge wie tunmyList[3].Value.someField += 7;
, aber wenn manmyList[3].Value
anderen Code gibt, erhält man den Inhalt von,Value
anstatt ihm ein Mittel zu geben, ihn zu ändern. Im GegensatzList<someStruct>
dazu wäre es notwendig , wenn man a verwendet , es zu verwendenvar temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Wenn ein veränderbarer Klassentyp verwendet wird,myList[3]
müssen alle Felder in ein anderes Objekt kopiert werden , um den Inhalt für externen Code verfügbar zu machen. Wenn man einen unveränderlichen Klassentyp oder eine Struktur im "Objektstil" verwendet, muss man eine neue Instanz erstellen, die ähnlich ist,myList[3]
außer dass siesomeField
anders ist, und diese neue Instanz dann in der Liste speichern.Ein zusätzlicher Hinweis: Wenn Sie eine große Anzahl ähnlicher Dinge speichern, kann es sinnvoll sein, sie in möglicherweise verschachtelten Arrays von Strukturen zu speichern, wobei Sie vorzugsweise versuchen, die Größe jedes Arrays zwischen 1 KB und 64 KB oder so zu halten. Arrays von Strukturen sind insofern besonders, als die Indizierung einen direkten Verweis auf eine Struktur innerhalb ergibt, so dass man "a [12] .x = 5;" sagen kann. Obwohl man Array-ähnliche Objekte definieren kann, erlaubt C # ihnen nicht, diese Syntax mit Arrays zu teilen.
quelle
Verwenden Sie Klassen.
Im Allgemeinen. Warum nicht den Wert b aktualisieren, während Sie sie erstellen?
quelle
Aus C ++ - Sicht stimme ich zu, dass das Ändern von Struktureigenschaften im Vergleich zu einer Klasse langsamer sein wird. Ich denke jedoch, dass sie schneller zu lesen sind, da die Struktur auf dem Stapel anstelle des Heaps zugewiesen wird. Das Lesen von Daten vom Heap erfordert mehr Überprüfungen als vom Stapel.
quelle
Wenn Sie sich schließlich für struct entscheiden, entfernen Sie den String und verwenden Sie einen Char- oder Byte-Puffer mit fester Größe.
Das ist re: Leistung.
quelle