Kennt jemand die Antwort und / oder hat er eine Meinung dazu?
Da Tupel normalerweise nicht sehr groß sind, würde ich annehmen, dass es sinnvoller wäre, für diese Strukturen als Klassen zu verwenden. Was sagst du?
performance
class
struct
.net-4.0
Bent Rasmussen
quelle
quelle
ValueTuple<...>
. Siehe Referenz bei C #Antworten:
Microsoft hat aus Gründen der Einfachheit alle Tupeltypen als Referenztypen festgelegt.
Ich persönlich halte das für einen Fehler. Tupel mit mehr als 4 Feldern sind sehr ungewöhnlich und sollten ohnehin durch eine typvollere Alternative ersetzt werden (z. B. ein Datensatztyp in F #), sodass nur kleine Tupel von praktischem Interesse sind. Meine eigenen Benchmarks haben gezeigt, dass Tupel ohne Box bis zu 512 Byte immer noch schneller sein können als Tupel mit Box.
Obwohl die Speichereffizienz ein Problem darstellt, glaube ich, dass das Hauptproblem der Overhead des .NET-Garbage-Collectors ist. Zuweisung und Erfassung sind in .NET sehr teuer, da der Garbage Collector nicht sehr stark optimiert wurde (z. B. im Vergleich zur JVM). Darüber hinaus wurde die Standard-.NET-GC (Workstation) noch nicht parallelisiert. Folglich kommen parallele Programme, die Tupel verwenden, zum Stillstand, da alle Kerne um den gemeinsam genutzten Garbage Collector kämpfen, wodurch die Skalierbarkeit zerstört wird. Dies ist nicht nur das Hauptanliegen, sondern AFAIK wurde von Microsoft bei der Untersuchung dieses Problems völlig vernachlässigt.
Ein weiteres Problem ist der virtuelle Versand. Referenztypen unterstützen Untertypen und daher werden ihre Mitglieder normalerweise über den virtuellen Versand aufgerufen. Im Gegensatz dazu können Werttypen keine Untertypen unterstützen, sodass der Aufruf von Mitgliedern völlig eindeutig ist und immer als direkter Funktionsaufruf ausgeführt werden kann. Der virtuelle Versand ist auf moderner Hardware sehr teuer, da die CPU nicht vorhersagen kann, wo der Programmzähler landen wird. Die JVM unternimmt große Anstrengungen, um den virtuellen Versand zu optimieren, .NET jedoch nicht. .NET bietet jedoch eine Flucht vor dem virtuellen Versand in Form von Werttypen. Die Darstellung von Tupeln als Werttypen hätte die Leistung hier wiederum dramatisch verbessern können. Zum Beispiel anrufen
GetHashCode
Bei einem 2-Tupel dauert eine Million Mal 0,17 Sekunden, beim Aufrufen einer äquivalenten Struktur jedoch nur 0,008 Sekunden, dh der Werttyp ist 20-mal schneller als der Referenztyp.Eine reale Situation, in der diese Leistungsprobleme bei Tupeln häufig auftreten, ist die Verwendung von Tupeln als Schlüssel in Wörterbüchern. Ich bin tatsächlich auf diesen Thread gestoßen, indem ich einem Link aus der Frage zum Stapelüberlauf gefolgt bin. F # führt meinen Algorithmus langsamer als Python aus! wo sich herausstellte, dass das F # -Programm des Autors langsamer war als sein Python, gerade weil er Boxed-Tupel verwendete. Durch manuelles Entpacken mit einem handgeschriebenen
struct
Typ wird sein F # -Programm um ein Vielfaches schneller und schneller als Python. Diese Probleme wären niemals aufgetreten, wenn Tupel zunächst durch Werttypen und nicht durch Referenztypen dargestellt worden wären ...quelle
Tuple<_,...,_>
Typen könnten versiegelt worden sein. In diesem Fall wäre kein virtueller Versand erforderlich, obwohl es sich um Referenztypen handelt. Ich bin neugieriger, warum sie nicht versiegelt sind, als warum sie Referenztypen sind.Der Grund ist höchstwahrscheinlich, dass nur die kleineren Tupel als Werttypen sinnvoll wären, da sie einen geringen Speicherbedarf hätten. Die größeren Tupel (dh diejenigen mit mehr Eigenschaften) würden tatsächlich an Leistung verlieren, da sie größer als 16 Bytes wären.
Anstatt einige Tupel Werttypen und andere Referenztypen zu sein, müssen Entwickler wissen, welche es sind. Ich würde mir vorstellen, dass die Leute bei Microsoft es für einfacher hielten, sie alle Referenztypen zu erstellen.
Ah, Verdacht bestätigt! Siehe Building Tuple :
quelle
Wenn die .NET System.Tuple <...> -Typen als Strukturen definiert wären, wären sie nicht skalierbar. Beispielsweise skaliert ein ternäres Tupel langer Ganzzahlen derzeit wie folgt:
Wenn das ternäre Tupel als Struktur definiert wäre, wäre das Ergebnis wie folgt (basierend auf einem von mir implementierten Testbeispiel):
Da Tupel eine integrierte Syntaxunterstützung in F # haben und in dieser Sprache extrem häufig verwendet werden, besteht für "struct" -Tupel bei F # -Programmierern das Risiko, ineffiziente Programme zu schreiben, ohne sich dessen überhaupt bewusst zu sein. Es würde so leicht passieren:
Meiner Meinung nach würden "Struktur" -Tupel mit hoher Wahrscheinlichkeit zu erheblichen Ineffizienzen bei der täglichen Programmierung führen. Andererseits verursachen die derzeit vorhandenen "Klassen" -Tupel auch bestimmte Ineffizienzen, wie von @Jon erwähnt. Ich denke jedoch, dass das Produkt aus "Eintrittswahrscheinlichkeit" mal "potenziellem Schaden" bei Strukturen viel höher wäre als derzeit bei Klassen. Daher ist die aktuelle Implementierung das geringere Übel.
Idealerweise gibt es sowohl "Klassen" -Tupel als auch "Struktur" -Tupel, beide mit syntaktischer Unterstützung in F #!
Bearbeiten (2017-10-07)
Strukturtupel werden jetzt wie folgt vollständig unterstützt:
quelle
ref
, oder die Tatsache nicht mag, dass sogenannte "unveränderliche Strukturen" dies nicht tun, insbesondere wenn sie in einer Box enthalten sind. Es ist schade, dass .net das Konzept der Übergabe von Parametern durch einen Durchsetzbaren nie implementiert hatconst ref
, da in vielen Fällen eine solche Semantik wirklich erforderlich ist.Dictionary
, z. B. hier: stackoverflow.com/questions/5850243 /…Für 2-Tupel können Sie immer noch das KeyValuePair <TKey, TValue> aus früheren Versionen des Common Type Systems verwenden. Es ist ein Werttyp.
Eine kleine Klarstellung des Artikels von Matt Ellis wäre, dass der Unterschied in der Verwendungssemantik zwischen Referenz- und Werttypen nur "geringfügig" ist, wenn die Unveränderlichkeit wirksam ist (was hier natürlich der Fall wäre). Dennoch denke ich, dass es im BCL-Design am besten gewesen wäre, nicht die Verwirrung darüber einzuführen, dass Tuple an einem bestimmten Schwellenwert auf einen Referenztyp übergeht.
quelle
Ich weiß nicht, aber ob Sie jemals F # -Tupel verwendet haben, sind Teil der Sprache. Wenn ich eine DLL erstellt und einen Typ von Tupeln zurückgegeben habe, wäre es schön, einen Typ zu haben, in den ich das einfügen kann. Ich vermute jetzt, dass F # Teil der Sprache ist (.Net 4). Einige Änderungen an CLR wurden vorgenommen, um einige gemeinsame Strukturen zu berücksichtigen in F #
Von http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records
quelle