* Zusammenfassung:
Bitte überprüfen Sie die sachkundigen Kommentare der Delphi-Experten. Speziell für mich würde ich versuchen, die alte TList / TObjectList wie von David vorgeschlagen zu verwenden und die Hard-Cast- und TObjectList.List-Eigenschaft zu verwenden, wie A.Bouchez vorgeschlagen hat. Ich werde versuchen TDynArray beim Refactoring in Zukunft.
================================================== =====================
Sagen Sie, dass ich eine Klasse TAtom
wie im folgenden Code definiert habe. Es gibt ungefähr hundreds
bis thousands
von TAtom-Instanzen zur Laufzeit, stored in a dynamic array
für jetzt. Zur Laufzeit muss ich für TAtom.X/Y/Z
aller vorhandenen TAtom-Instanzen mehr als 30
mal pro Sekunde einfache float-Berechnungen durchführen.
Nun muss ich zur Laufzeit die Fähigkeit von adding
, inserting
, deleting
von TAtom-Instanzen hinzufügen. Es scheint, dass meine Wahl (1) ein großes Array anfordern; (2) Bleiben Sie bei dynamischen Array und manuell SetLength; (3) Wechsel zu regulärer TList; (4) Wechsel zur regulären TObjectList.
Ich möchte (1) vermeiden, wenn es nicht notwendig ist, weil ich dann ziemlich viele Funktionssignaturen ändern muss. (2) sieht auch nicht gut aus, denn TList / TObjectList scheint für diese Aufgabe geboren zu sein. Da jedoch Typ-Casting mit der regulären TList / TObjectList benötigt wird, könnte jemand den möglichen Leistungseinbruch kommentieren? Ich meine, es wäre am besten, wenn die Leistungsbelastung abgeschätzt werden könnte, bevor ich den Code umschreibe. Wenn die Leistung merklich nachlässt, gibt es andere Techniken, die ich verwenden könnte?
Außerdem frage ich mich, ob es einen Leistungsunterschied zwischen der Verwendung von TList und TObjectList gibt?
%Vor% Wenn du Generics.Collections.TObjectList<TAtom>
benutzt und kein Casting brauchst.
Die Leistung sollte für die von Ihnen beschriebene Verwendung in Ordnung sein. Das Einfügen ist anspruchsvoller als das Hinzufügen am Ende, weil Sie die Elemente nach dem Einfügepunkt in der Liste nach oben verschieben müssen.
Solange Sie SetLength(A, Length(A)+1)
vermeiden und sich für eine sinnvollere Zuweisungsstrategie entscheiden, sind dynamische Arrays äquivalent zu allen TList
like-Klassen.
Gelegentlich hatte ich Probleme mit der Leistung und der Speicherfragmentierung, wenn ich versuchte, große Listen als zusammenhängende Speicherblöcke zu verwalten. Dann habe ich auf ein Unterzuteilungsschema zurückgegriffen. Da Ihre Listen jedoch Objektverweise enthalten, die im Wesentlichen Zeiger sind, haben Sie bereits eine implizite Unterzuweisung.
Es ist alles etwas spekulativ und Sie müssen wirklich messen - sonst können wir nur raten.
Darf ich Ihrer Liste eine weitere Auswahl hinzufügen?
Wenn Sie für die Daten in TAtom
keine Vererbungsfunktion verwenden, können Sie record
anstelle von class
verwenden. Jede Klasseninstanz muss im Speicher reserviert, mit Null gefüllt und einzeln initialisiert werden. Getmem/Freemem
kostet immer, und die Speicherfragmentierung wird zunehmen.
Ein vorab zugeordnetes dynamisches array of record
ist schneller als einzelne Klasseninstanzen zum Hinzufügen. Und die Daten werden besser für den CPU L1 / L2 Cache passen.
Beim Einfügen und Löschen ist ein Array solcher Datensätze langsamer als TList
, wenn Sie eine große Anzahl von Elementen haben, da mehr Daten gelöscht / eingefügt werden müssen ( TList/TObjectList
beide pflegen nur eine Liste von Zeiger). Für ein noch schnelleres Einfügen / Löschen sollten Sie besser eine verkettete Liste verwenden.
Aufgrund der internen Benachrichtigung gibt es im TList/TObjectList
-Mechanismus einen gewissen Mehraufwand. Mechanismus Und die GetItem()
-Eigenschaft könnte etwas langsamer sein (wegen Bereichsüberprüfung), als direkt ein dynamisches Array zu verwenden.
Aber mit unserem TDynArray-Wrapper könnten Sie bei einem dynamischen Array bleiben und trotzdem eine gute Leistung erzielen , Vorbelegungsfunktionen und TList
-ähnliche Methoden. Und noch mehr Methoden wie SaveToStream, Slice, Reverse
, Sortierung mit externen Indizes und so ...
Arbeitet mit Delphi 6 bis XE.
Mit neueren Versionen von Delphi, die Generika unterstützen, sollten Sie besser in diese Richtung gehen.
Allerdings, weil Typ-Casting ist benötigt mit dem regulären TList / TObjectList, könnte jemand Kommentar zur möglichen Leistung getroffen?
Wenn Sie Cast mit dem Formular
eingeben %Vor%Es wird ein kleiner Overhead hinzugefügt, der sich in Ihrer Situation wirklich summieren kann. Wenn Sie jedoch hart tippen,
%Vor%Soweit ich weiß, kommt es tatsächlich nicht zu einem Overhead, da die Typumwandlung ohne Überprüfung durchgeführt wird und ich glaube auch, dass dies zur Kompilierungszeit geschieht.
Was den anderen Aspekt Ihrer Frage anbelangt, denke ich, dass sie alle bereits richtig behandelt wurden ...
Erste Frage: Sprechen wir über Classes.TList
und Contnrs.TObjectList
oder reden wir von Generics.Collections.TList
bzw. Generics.Collections.TObjectList
?
Wenn wir über Generika sprechen, werden TList und TObjectList mit dynamischen Arrays implementiert. Wenn es einen Leistungsunterschied zwischen der direkten Verwendung eines dynamischen Arrays oder der Verwendung der netteren Schnittstelle des generischen Containers gibt, wird dies vernachlässigbar sein.
Wenn wir von den "älteren" TList
und TObjectList
sprechen, müssen wir nur TList
mit der dynamischen Array-Entsprechung vergleichen, da TObjectList
ein Nachkomme von TList
ist, also erbt all das sind die Leistungsmerkmale. TList
verwendet einen Speicherblock, der mit ReallocMem
zugewiesen wurde. Das dynamische Array macht intern dasselbe, also sollte es keinen signifikanten Unterschied geben!
Wenn es einen Leistungsunterschied zwischen den beiden gibt, ist es wahrscheinlich, weil die naive Verwendung von dynamischem Array das gefürchtete SetLength(A, Length(A)+1)
verwendet, während die bessere Implementierung in allen von Delphi bereitgestellten Containern Speicher in größeren Blöcken vorreserviert. Mit dem richtigen Code sollte es keinen signifikanten Unterschied zwischen diesen Alternativen geben!
Erstellen Sie ein Testprojekt und messen Sie den Zeitpunkt des Hinzufügens, Einfügens und Löschens von Tausenden von TAtom-Instanzen mithilfe der vier Methoden. Entscheide dann, welches du verwenden sollst.
TList und TObjectList sind wahrscheinlich beim Hinzufügen, Einfügen und Löschen schneller als ein dynamisches Array, da das dynamische Array ständig neu zugewiesen werden muss. Die Implementierung von TList und TObjectList macht das nicht.
TList
usw. tun genau das, was Code, der an Chunks von Speicher oder Dynarrays arbeitet, tun müsste, aber ihre Implementierung ist bereits für den allgemeinen Fall optimiert und enthält Überlegungen darüber, wie sich der Speichermanager verhält.
Ein Kriterium könnte das Verhältnis von Lesevorgängen / Aktualisierungen zu der Sequenz sein. Wenn die Sequenz nach der Erstellung selten aktualisiert wird, sollte eine höhere Geschwindigkeit mit Dynarrays erkennbar sein, da der Zugriff auf Elemente mit TList
und ähnlichen Objekten einen Methodenaufruf plus Schrankenprüfung sowie eine Typprüfung erfordert, wenn Sie den Operator as
verwenden .
Am Ende sollten die Kosten der Arithmetik, die auf TAtom
ausgeführt werden, die Laufzeit dominieren, was die Wahl von Dynarray oder TListXXX
irrelevant macht.
Ein dynamisches Array von Datensätzen hat die geringste Auswirkung beim Zugriff auf die Elemente, wenn Ihre Atome Objekte sind, dann werden alle Listen in Bezug auf die Zugriffsgeschwindigkeit etwas äquivalent sein.
Wenn Sie jedoch viele von ihnen ausführen, wird Ihr Schlüsselproblem die Einfügungen und Löschungen sein, für die alle Listen und Arrays schlecht funktionieren, aber genau das sagt Ihnen die Profilerstellung. Wenn dies der Fall ist, möchten Sie vielleicht Folgendes in Erwägung ziehen:
Tags und Links arrays delphi tlist tobjectlist