![]() |
Delphi-Version: 11 Alexandria
Warnung vor massivem Einsatz von Generics in Delphi
Hallo zusammen,
ich überlege schon länger, ob ich diesen Post verfassen und meine Erkenntnisse mit der Community teilen sollte. Heute habe ich mich schließlich dazu überwunden. Unsere Vorgeschichte: Wir setzen aktuell Delphi 11.3 ein. Vor einigen Jahren wurde im Rahmen der Modernisierungsarbeiten eine interne Empfehlung herausgegeben, wann immer möglich, generische Typen, entweder aus der internen Basisbibliothek oder aus dem Spring-Framework abzuleiten und zu verwenden. Grundsätzlich ist dieser Schritt begrüßenswert und richtig. Doch keiner aus unserem Team konnte zu diesem Zeitpunkt ahnen, welche langfristigen Folgen dies haben wird. Die Folgen sind:
Ich bin ein Fan von Generics, aber unter diesen Umständen kann ich vom weiträumigen Einsatz in großen Delphi-Projekten nur abraten! Das Spring4D-Framework, so sehr ich es auch mag, sticht hier leider nochmals unangenehm hervor, wahrscheinlich weil die Ableitungshierarchien sowohl in den Interfaces als auch in den implementierenden Klassen tief verschachtelt sind. In Spring4D v2 hat Stefan schon sehr viel gegen den Code-Bloat getan, aber gegen den hohen RAM-Verbrauch des Compilers/Linkers kann er nicht viel tun. Dies kann nur Embarcadero fixen. So, jetzt habe ich mal meinen Frust hier kund getan und bin gespannt, ob es irgendjemandem hier auch so ergeht? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
![]() |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Ich danke dir für deinen Post. Geht mir auch alles so.
Ich find's interessant, dass sich nicht die DCU aufbläht, wo Variablen mit der generischen Klasse deklariert werden, sondern eine andere DCU, die viele von diesen Variablen genutzt werden - obwohl zu dem Zeitpunkt schon klar ist, welche Typen damit verwendet werden und welche nicht. Die Erklärung ist gut. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Das war auf jeden Fall interessant zu lesen und kurzweilig erklärt 👍
Wir sind glücklicherweise noch nicht an dem Punkt wo dem Compiler/Linker grundsätzlich der Speicher ausgeht, aber vor allem die Linker-Zeiten machen wirklich echt keinen Spaß mehr. Gibt es denn, außer dem Versuch die Toolkette auf 64 Bit umzustellen um das Problem zu verdecken, noch andere Entwicklungen in den letzten Jahren? Unsere Hauptanwendung ist immer noch auf Delphi 10.0 Seattle - Ist es in den letzten Versionen eher besser, schlechter oder unverändert? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Ohhhh, Danke dass du dir die Arbeit gemacht hast.
Wir verwenden 11.1. Dessen LSP mag scheinbar Generics generell nicht sonderlich. Zum Einsatz kommen die eigentlich fast nur wo es Delphi gerne so haben möchte. Alles andere decke ich mit der guten alten TStringList oder Memtables ab. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Äh, nur um sicherzugehen:
Die generischen Typen selbst, nicht die Klassen aus der Generics-unit sind das Problem, oder? D.h. bei TList<T> ist "T" das Problem, und nicht die generische Liste selbst, bzw eine TList<TWasAuchImmer> mit einem spezifischen Typ TWasAuchImmer wäre unproblematisch? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Aber wie schon Eingangs geschrieben, wird es erst zum Problem, wenn die Masse an solchen generischen Entitäten eine gewisse Schwelle überschreitet. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
@WladiD: Ist das mit der Spring4D v2 immer noch so? Ich habe da das im Kopf (siehe weiter unten nach den Benchmarks:
![]() |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Hallo WladiD,
insgesamt hast Du recht, angesichts der Architektur von Generics im Delphi-Compiler (und der Kernentscheidung, sie zu einem Kompilierungszeit-Tool ohne Laufzeitunterstützung zu machen) verursachen sie eine erhebliche Code-Aufblähung. Wir haben uns mit einigen Optimierungen befasst, und es gibt Raum für Verbesserungen bei der Kompilierungszeit und der Speichernutzung, aber solange wir nicht das gesamte System neu gestalten, werden die Verbesserungen in das aktuelle Modell fallen. Entwicklern steht eine sehr wichtige Lösung, ein Workaround oder ein Hack zur Verfügung: die wiederholte Deklaration für dieselbe generische Typinstanz vermeiden. Zur Verdeutlichung: Ein generischer Typ ist TList<T>, eine generische Typinstanz ist TList<Integer>. Wenn man TList<Integer> in Unit A und Unit B hat, dann hat man zwei Typen, wobei alle Methoden in jede der beiden (oder 200) Units kopiert werden. Wenn man die Unit C erstellt, TList<Integer> dort einfügt, ihr einen bestimmten Typnamen gibt und diese Unit und diesen Typ verwendet, greift das Ganze auf die allgemeine Typverwendung zurück. Der Unterschied ist meines Wissens nach sehr signifikant und wir wissen, dass es bei vorhandenem Code etwas mühsam ist... |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Vielen Dank, das ganze Thema ist sehr anschaulich und hilfreich 👍
In meinem Fall bin ich mir unsicher abzuschätzen, ob das ganze in unserer Codebasis überhaupt schon ein nennenswertes Problem ist, oder in wie ferner Zukunft es problematisch werden könnte. Ich kann mich erinnern, dass es ein Tool gab, das DCU-Dateien analysiert und darüber eine Aussage treffen konnte. Hat jemand den Namen noch parat? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Wirkt sich das Aufblähen der DCUs eigentlich auch auf die Größe der EXE aus?
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Wenn man nun sehr viele solcher Typen in den einzelnen Units hat, ist der Aufwand des Ersetzens sehr hoch. Und da die nötigen Informationen im Speicher benötigt werden, wird dafür viel Arbeitsspeicher benötigt. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Habe ich es dann richtig verstanden? Statt
Delphi-Quellcode:
Wäre es dann also besser es so zu machen?
TCountList = TList<Integer>;
TWidthList = TList<Integer>; TLengthList = TList<Integer>;
Delphi-Quellcode:
TIntegerList = TList<Integer>;
TCountList = type TIntegerList; TWidthList = type TIntegerList; TLengthList = type TIntegerList; |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Es wäre auch viel zu einfach, die jeweiligen TypBezeichner im Generic schnell zum voll-qualifizierten Namen auflösen und diesen Namen dann in iner kleinen Liste zu speichern und bei der Generic-Deklaration anfangs in der Liste zu schauen,
anstatt erstmal zu kompilieren und dann in einer fetten Liste nach dem Resultat zu suchen. :roll::stupid: |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Hat Delphi 12.2 nicht einen 64 Bit Compiler und Linker erhalten, der aber idiotischerweise nur in der Enterprise+ Version enthalten ist. Ist damit das Memory Problem nicht gelöst? Oder mache ich hier ein Durcheinander?
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Jupp, es gibt einen zweiten Win64-Compiler und nur in Enterprise (nicht CE und Professional), und auch nur für C++Builder (glaub i) ... auch wenn dennoch in den Settings überall "Win64 (modern)" zu finden ist ... schon länger, selbst wenn ausschließlich Delphi (nicht RAD Studio) installiert ist. :freak:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Wir haben hier ja zum Einen was wir für eine Output gegenerieren wollen und zum Anderen eben neu, wie der Compiler/Linker selber compiliert ist, also 32 Bit oder bei den neuen mit 64 Bit. Bisher war wohl der Compiler und Linker 32 Bit Programme inkl 4GB Memorybegerenzung. Die neuen 64 Bit Compiler sind in nativem 64 Bit compiliert und haben damit diese Memorygrenze nicht mehr. Dass da die Doku von Win64 etc. redet ist ja so schon korrekt, denn auch bisher konnte man ja selber 32 + 64 bit Programme erzeugen.
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Das Problem dabei ist jedoch, dass ein entsprechend großes Projekt eine große RSM-Datei erzeugt. Diese wird auch zum lokalen Debuggen benötigt, wenn man das Projekt über MS-Build erzeugt. Das heißt, im ungünstigen Fall kann man vielleicht wieder kompilieren, aber nicht mehr debuggen, weil die IDE in ihr Speicherlimit reinläuft. Bei uns ist gleich beim ersten Debug-Versuch die bds.exe bei über 2GB. Nach einigen weiteren Debug-Versuchen kommt die geliebte EOutOfMemory... |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Jupp, alles, was nicht 32 Bit ist, muß ja remotegedebuggt werden, da die IDE und der InlineDebugger ja nur 32 Bit sind.
Vielleicht, wenn in 25 Jahren dann wieder, wenn die IDE auch 64 Bit ist. Wobei es dennoch irgendwie komisch ist, dass die "gleichen" Debuginfos jeweils so unterschiedlich groß sind. Dass eine billige ZIP-Komprimierung aber so extrem viel einspart, lässt einen irgendwie denken, dass da viel "nichts" drin steckt. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Naja, ich glaube schon, dass wir viel früher als in 25 Jahren eine 64 Bit IDE sehen werden.
Nur wäre es natürlich klasse, wenn man den Speicherbedarf auch ohne Wechsel auf 64 Bit senken könnte. Die Methode "viele Hardware Ressourcen auf ein Problem zu schmeißen" ist ja auch nicht gerade umweltfreundlich und kann auch an ihre Grenzen stoßen... |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Sie haben eine lauffähige 64-Bit IDE, die aber noch nicht stabil genug ist. Sie werden diese in den nächsten Monaten so weit haben, dass man damit zumindest testen kann. Und als erstes müssen ja z.B. die Komponentenhersteller ran. Von daher gehe ich mal davon aus, dass eine wirklich relativ normal für alle nutzbare 64-Bit IDE Richtung 2026 kommen könnte. Und dazwischen irgendwann eine Beta für alle. Wie gesagt: Das ist meine Erinnerung und Interpretation dessen, was ich dort gehört habe. Zitat:
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Zitat:
![]() Zum allgemeinen Thema: Hab ich schon 2014 bzw 2017 gesagt - siehe: ![]() ![]() Zitat:
Zitat:
Schick mir gern eine pm oder email, wenn ihr diesbezüglich Hilfe von mir benötigt. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Delphi-Quellcode:
TIntegerList = TList<Integer>;
TCountList = class(TIntegerList); TWidthList = class(TIntegerList); TLengthList = class(TIntegerList); |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
den Workaround über einen Typ-Alias für generische Interfaces hat ein Kollege schon vor einem Jahr probiert. Die Erkenntnis war damals, dass es nichts gebracht hat. Ich habe auch ein ähnliches Experiment in einem isolierten Test-Projekt durchgeführt und bin zum gleichen Ergebnis gekommen, aber ich muss zugeben, dass meine primäre Metrik die Größe der resultierenden Exe war und nicht die Größe der einzelnen DCUs. Dies ist wiederum auf die gute Arbeit des Linkers zurückzuführen. Gestern hat ein Kollege, auf ihren Post hin, wieder ein Testprojekt gebaut und dort kann man tatsächlich feststellen, dass ein Alias IListString = IList<String> etwas gebracht hat, wenn die zugehörige Factory (TCollections.CreateList<String>) nicht in der konsumierenden Unit vorkommt, sondern über eine eigene Factory-Funktion in einer dedizierten Unit bezogen wird. Das ist schon mal eine interessante neue Erkenntnis. Da ich gerade dabei bin... Wie ist Ihre Aussage bezüglich einer 64-Bit-IDE? Wann gibt es ungefähr die erste Beta? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Und hilft das? |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Zitat:
Delphi-Quellcode:
Hintergrund ist, dass entscheidend ist, wo der generische Parameter aufgelöst wird. Diese DCU enthält entsprechend den Code. Wenn man einen so aufgelösten abgeleiteten Typ ohne generischen Parameter anderswo verwendet, enthält die betreffende Unit auch nicht mehr den Code der ursprünglichen generischen Klasse, selbst wenn man weitere Klassen davon ableitet.
TCountList = class(TList<Integer>);
TTextList = class(TList<string>); |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Das Problem ist, dass generische Typen eine flexiblere Typkompatibilitätsregel bieten und mit der Typdeklaration wird diese Regel verletzt. Der Vorteil ist jedoch, dass man am Ende eine einzige Instanz des generischen Typs hat, viel weniger Code, schnellere Kompilierung und Verknüpfung... Ich weiß, dass dies nicht ideal ist, aber ich bezweifle, dass sich dies in nächster Zeit ändern wird.
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
![]() Zitat:
|
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Wenn du aber irgendwelche generische Typen verwendest, die in nicht verbundenen Units liegen, müssen diese in der DCU enthalten bleiben, auch wenn der gleiche generische Parameter in 100 Units verwendet wird. Das kann dann erst der Linker lösen. Denn der Compiler kann ja nicht einfach in eine DCU einen Verweis auf eine fremde DCU einbauen, nur weil diese gerade mit kompiliert wird und den gleichen Typ enthält. Rein theoretisch könnnte ich mir vorstellen, dass der Compiler die konkreten Typen in eigene DCUs auslagert, die dann von mehreren DCUs verwendet werden könnten. Die nötige Funktionalität gibt es aktuell aber nicht und da hängt dann noch einiges mehr dran, angefangen bei der Benennung dieser Dateien. Trotzdem wäre es die einzige Lösung, die mir einfällt, wie das ganze überhaupt allgemein lösbar sein könnte, sprich ohne dass der Linker das alles noch einmal komplett aussiebt. |
AW: Warnung vor massivem Einsatz von Generics in Delphi
Zitat:
Delphi-Quellcode:
und
TList<TApfel>
Delphi-Quellcode:
habe (beides sind Klassen), gibt es den code für
TList<TBirne>
Delphi-Quellcode:
2mal in der Binary (einmal für TApfel und einmal für TBirne) obwohl er komplett identisch ist. Bei Spring4d foldet er das intern auf die implementierung von TList<TObject>. Das geht, da die collections Interface basiert sind.
TList<T>
Delphi-Quellcode:
und
IList<TApfel>
Delphi-Quellcode:
erzeugen nur wenig Binärcode und dahinter hängt dann jeweils dieselbe implementierung (mehr oder weniger, gibt noch einige Details, die hier aber irrelevant sind).
IList<TBirne>
Schwierig wird es dann bei assoziativen Collections, also solchen, wo man 2 generische Parameter hat, da sich die Kombinationen dann multiplizieren. Das kann man inzwischen sehr gut sehen, wenn man eine FMX Anwendung oder eine Anwendung mit einer Drittanbieter Bibliothek verwendet, die auch ausgiebig Gebrauch von Generics machen, z.B. DevExpress. Da hat man dann mitunter einen zweistelligen Prozentanteil an Binärgröße nur mit Zeugs aus System.Generics.Collections verbraten. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz