Wie groß ist der Leistungsunterschied zwischen TList, TObjectList und Plain Array, wenn er geschätzt werden könnte?

8

* 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%     
SOUser 18.03.2011, 12:32
quelle

8 Antworten

4

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.

    
David Heffernan 18.03.2011, 12:49
quelle
6

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 ...

%Vor%

Arbeitet mit Delphi 6 bis XE.

Mit neueren Versionen von Delphi, die Generika unterstützen, sollten Sie besser in diese Richtung gehen.

    
Arnaud Bouchez 18.03.2011 13:01
quelle
3
  

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 ...

    
Ken Bourassa 18.03.2011 14:22
quelle
1

Da TObjectList ein direkter Nachkomme von TList ist, sind die Leistungen sehr nah.

    
philnext 18.03.2011 12:42
quelle
1

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!

Fazit

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!

    
Cosmin Prund 18.03.2011 12:48
quelle
1

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.

    
The_Fox 18.03.2011 12:42
quelle
1

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.

    
Apalala 18.03.2011 13:02
quelle
1

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:

  • eine verknüpfte Liste, wenn Sie nicht auf Atome nach Index
  • zugreifen müssen
  • ein Baum, sollten Sie für eine Raumpartition Ihrer Atome verwenden, können Sie diese Partition genauso gut für Ihre Atome als für das Array
  • verwenden
  • erlaubt undefinierte / nil-Elemente in Ihrem Array / in Ihrer Liste und verwaltet einen Stapel von undefinierten / nil-Elementen und einen Index, wenn Sie eine sortierte Liste benötigen (möglicherweise die leistungsstärkste Lösung, aber wahrscheinlich auch die komplexeste, um direkt hineinzukommen) Begriffe der Effizienz)
Eric Grange 18.03.2011 13:29
quelle

Tags und Links