![]() |
Code Optimisation: Benutzung von const in prozedur-Köpfen
Kürzlich habe ich irgendwo nebenbei gefragt, was const in Prozedurköpfen bringt.
Geantwortet wurde, dass der Code dadurch wohl optimierter sei. Kann man also festhalten, dass wenn ich die Parameter nicht innerhalb der Prozedur veränderte, ich überall const verwenden kann? Für Integer, Boolean, Strings und Typen? |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ja, kannst du. Es schadet zumindest nicht. Bei Typen wie Integer, Char, Boolean etc. macht es aber in der Praxis keinen Unterschied, da eh immer der Wert kopiert wird. Bei größeren Typen wie Records kann const einiges ausmachen, weil dann nur noch ein Pointer übergeben wird, was schneller ist, als den ganzen Record zu kopieren. Und bei referenzgezählten Typen wie Strings spart man sich durch const ein addref und ein release.
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ganz so trivial ist es aber leider auch nicht. Wenn du z.B. eine Klasse als const-Parameter übergibst, kannst du nur den Pointer nicht ändern, den Inhalt aber schon. Ich bin da auch schon drüber gestolpert, weil ich es von C++ anders kannte.
Auch führt folgendes zu m.E. seltsamen Ergebnissen:
Delphi-Quellcode:
var FString: String = 'Test';
procedure Change(const AString: String); begin FString := 'Blubb'; ShowMessage(AString); end; procedure CallMe; begin Change(FString); Change(FString); end; |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Wie macht das den C? Den Zugriff auf die Globale Variable verhindern, weil das zufällig die gleiche Referenz ist? Fehlermeldung beim compilieren? Oder etwa zur Laufzeit? Mavarik |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Zitat:
Ich handhabe es immer so, dass ich nur Variablen/Typen etc übergebe bei denen ich ganz genau weiß, dass ich diese in der Prozedur / Funktion nur lese und nicht schreibe. Dann sollte eigentlich nichts schiefgehen. Auch nutze ich das nur bei String und Typen. Zahlenwerte und Booleans ignoriere ich. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ich denke Const ist nicht nur für die Performance, sondern auch (für mich) so einen Art Flag...
Signalisiert mir
Delphi-Quellcode:
ändert nix an dem was ich Übergebe...
Procedure Foo(Const AValue : Integer)
Genauso wie OUT klar kann man Var nehmen aber das verhalten wird eindeutiger... |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Delphi-Quellcode:
hätte erhält sie lokal auf dem Stack eine Kopie des Parameters, aber was kümmert das den Aufrufer?
const
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Delphi-Quellcode:
könnte es sein, dass die procedure Foo AValue intern verändert. Um das raus zu bekommen, muss ich mir die Procedure anschauen... Wenn da jedoch
Procedure Foo(Var AValue : integer)
Delphi-Quellcode:
steht, ist es klar, meine Variable bleibt erhalten... (Halte Dich jetzt nicht am Integer="Welchertyp auch immer" fest) (Abgesehen von Classen-Object-Inhalte die über Methoden verändert werden...)
Procedure Foo(Const AValue : Integer)
Warum hat developer X Var geschrieben? Weil er nicht wollte, das die ganze Struktur einmal kopiert werden muss... (Der kannte kein Const) Ich habe selber noch Millionen Zeilen Source-Code mit Var statt Const. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Im Bezug auf Strings hatte ich vor kurzem schonmal auf eine ähnliche Frage geantwortet:
![]() Zitat:
Zitat:
Delphi-Quellcode:
nicht nach dem Funktionsaufruf, aber z.b. Objektparameter deklariere ich auch gerne mit
const
Delphi-Quellcode:
, um zu signalisieren, dass ich das Objekt nicht verändere (auch wenn das aufgrund der fehlenden const correctness von Delphi natürlich funktionieren würde).
const
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ok ich wollte nur darauf hinaus dass ein
Delphi-Quellcode:
für den Aufrufer genauso viel aussagt wie kein
const
Delphi-Quellcode:
.
const
Aus Performance-Gründen haben wir das
Delphi-Quellcode:
drin wo geht, es als Doku für Arme zu nutzen dass man verspricht das übergebene Objekt nicht zu manipulieren wäre natürlich auch eine Möglichkeit. Leider ist es dafür zu spät. Wäre vielleicht wirklich eine gute Idee gewesen. Naja, für's nächste Projekt... ;-)
const
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Sobald const,var und out im Spiel sind, weiß ich, das nicht der Wert übergeben wird.
Aber das
Delphi-Quellcode:
das gleiche ist wie
var Myvariable
Delphi-Quellcode:
muß ich mir immer wieder ins Bewußtsein holen. Meiner Meinung nach ist das nicht so Optimal.
@Myvariable
Gruß K-H |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ehrlich gesagt, verstehe ich die Verwirrung nicht: Man googele doch bitte mal CallByValue, CallByReference und CallByName. Das sind uralte Techniken. Das CONST soll doch das bei CallByValue obligatorische Kopieren des Wertes auf den Stack verhindern, um den nicht unnötig vollzumüllen und macht daher nur bei komplexen Datenstrukturen sowie in Delphi bei Strings Sinn. VAR resp. OUT bedeuten dagegen CallByReference, d.h. es wird die Adresse übergeben und damit ist der Wert der Variablen außerhalb der Prozedur veränderbar. CallByName kennt Delphi glücklicherweise nicht. Das gab es in ALGOL60, mit teilweise verblüffenden Ergebnissen.
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Ja klar ist das uralt, aber warum zum Teufel gibt es dann kein
Delphi-Quellcode:
sondern nur ein
refconst Myvalue
Delphi-Quellcode:
? Wahrscheinlich dient es der Klarheit :mrgreen:
const Myvalue
Gruß k-H |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Delphi-Quellcode:
(
const [Ref]
![]() ![]() |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Grund: Durch das Create selbst wird logischerweise der Referenzzähler nicht erhöht, durch das const ist aber auch die Referenzzählung für den Parameter deaktiviert. Also steht der Referenzzähler auf 0. Übergibt nun der Konstruktor das Interface an eine weitere Routine mit Referenzzählung, wird die Referenz um 1 erhöht und wieder auf 0 gesetzt. Danach ist dann die Referenz ungültig. Solange man sauber immer mit Zwischenvariablen arbeitet, ist aber alles in Ordnung. So einen Fehler findet man aber leider nicht unbedingt so schnell... |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Mavarik |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Delphi-Quellcode:
wie CallByValue:
const
Delphi-Quellcode:
Bei komplexen Datentypen (Objekte, Records, Interfaces, Strings) dahingegen wie CallByReference (dann wird allerdings auch der Parameter, welcher komplett ohne
Unit1.pas.45: A(X);
005CDEEF 8B45F8 mov eax,[ebp-$08] // CallByValue 005CDEF2 E8D9FFFFFF call A // procedure A(X: Integer) Unit1.pas.46: B(X); 005CDEF7 8D45F8 lea eax,[ebp-$08] // CallByReference 005CDEFA E8D5FFFFFF call B // procedure B(var X: Integer) Unit1.pas.47: C(X); 005CDEFF 8B45F8 mov eax,[ebp-$08] // CallByValue 005CDF02 E8D1FFFFFF call C // procedure C(const X: Integer)
Delphi-Quellcode:
oder
const
Delphi-Quellcode:
deklariert wurde als CallByReference umgesetzt. Der einzige Unterschied zwischen
var
Delphi-Quellcode:
und "nichts" besteht hier wirklich nur in der Schreibschutzprüfung - welche wie du bereits vermutet hast - zur Compiletime umgesetzt wird):
const
Delphi-Quellcode:
Hierbei ist Delphi sogar so klug, Records mit z.b. nur einem einzigen Integer Element trotzdem als trivialen Datentyp zu behandeln.
Unit1.pas.52: A(X);
005CDEEF 8D45F4 lea eax,[ebp-$0c] // CallByReference 005CDEF2 E8D9FFFFFF call A // procedure A(X: TStruct) Unit1.pas.53: B(X); 005CDEF7 8D45F4 lea eax,[ebp-$0c] // CallByReference 005CDEFA E8D5FFFFFF call B // procedure B(var X: TStruct) Unit1.pas.54: C(X); 005CDEFF 8D45F4 lea eax,[ebp-$0c] // CallByReference 005CDF02 E8D1FFFFFF call C // procedure C(const X: TStruct) Die eigentliche Optimierung sieht man im Falle des Structs hier ganz schön (jeweils komplett leere Funktionen):
Delphi-Quellcode:
Und bei Strings sieht die Sache ähnlich aus (ebenfalls jeweils komplett leere Funktionen):
// procedure A(X: TStruct)
Unit1.pas.32: begin 005CDED0 55 push ebp 005CDED1 8BEC mov ebp,esp 005CDED3 81C46CFEFFFF add esp,$fffffe6c 005CDED9 56 push esi 005CDEDA 57 push edi 005CDEDB 8BF0 mov esi,eax 005CDEDD 8DBD6CFEFFFF lea edi,[ebp-$00000194] 005CDEE3 B965000000 mov ecx,$00000065 005CDEE8 F3A5 rep movsd Unit1.pas.34: end; 005CDEEA 5F pop edi 005CDEEB 5E pop esi 005CDEEC 8BE5 mov esp,ebp 005CDEEE 5D pop ebp 005CDEEF C3 ret // procedure B(var X: TStruct) Unit1.pas.37: begin 005CDEF0 55 push ebp 005CDEF1 8BEC mov ebp,esp 005CDEF3 51 push ecx 005CDEF4 8945FC mov [ebp-$04],eax Unit1.pas.39: end; 005CDEF7 59 pop ecx 005CDEF8 5D pop ebp 005CDEF9 C3 ret // procedure C(const X: TStruct) Unit1.pas.42: begin 005CDEFC 55 push ebp 005CDEFD 8BEC mov ebp,esp 005CDEFF 51 push ecx 005CDF00 8945FC mov [ebp-$04],eax Unit1.pas.44: end; 005CDF03 59 pop ecx 005CDF04 5D pop ebp 005CDF05 C3 ret
Delphi-Quellcode:
// procedure A(X: String)
Unit1.pas.32: begin 005CDED0 55 push ebp 005CDED1 8BEC mov ebp,esp 005CDED3 51 push ecx 005CDED4 8945FC mov [ebp-$04],eax 005CDED7 8B45FC mov eax,[ebp-$04] 005CDEDA E881BEE3FF call @UStrAddRef 005CDEDF 33C0 xor eax,eax 005CDEE1 55 push ebp 005CDEE2 6803DF5C00 push $005cdf03 005CDEE7 64FF30 push dword ptr fs:[eax] 005CDEEA 648920 mov fs:[eax],esp Unit1.pas.34: end; 005CDEED 33C0 xor eax,eax 005CDEEF 5A pop edx 005CDEF0 59 pop ecx 005CDEF1 59 pop ecx 005CDEF2 648910 mov fs:[eax],edx 005CDEF5 680ADF5C00 push $005cdf0a 005CDEFA 8D45FC lea eax,[ebp-$04] 005CDEFD E87ABDE3FF call @UStrClr 005CDF02 C3 ret 005CDF03 E990B3E3FF jmp @HandleFinally 005CDF08 EBF0 jmp $005cdefa 005CDF0A 59 pop ecx 005CDF0B 5D pop ebp 005CDF0C C3 ret // procedure B(var X: String) Unit1.pas.37: begin 005CDF10 55 push ebp 005CDF11 8BEC mov ebp,esp 005CDF13 51 push ecx 005CDF14 8945FC mov [ebp-$04],eax Unit1.pas.39: end; 005CDF17 59 pop ecx 005CDF18 5D pop ebp 005CDF19 C3 ret // procedure C(const X: String) Unit1.pas.42: begin 005CDF1C 55 push ebp 005CDF1D 8BEC mov ebp,esp 005CDF1F 51 push ecx 005CDF20 8945FC mov [ebp-$04],eax Unit1.pas.44: end; 005CDF23 59 pop ecx 005CDF24 5D pop ebp 005CDF25 C3 ret |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Das mit der Beschreibbarkeit ist auch korrekt. Das passiert komplett zur Compiletime - sowohl für CallByValue, als auch für CallByReference. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Und nein, denn wenn das schon zu Beginn 1 ist und man übergibt das Interface an eine Variable, dann wäre es danach 2. Ist die Variable dann weg, wäre es aber immernoch 1 und würde niemals freigegeben. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Ich habe in C schon manuelle Referenzzählung implementiert und dort habe ich es immer so gemacht:
Code:
So sollte es der Compiler auch machen.
typedef struct RefCounted {
int refcount; } RefCounted; RefCounted* refcounted_new() { RefCounted *obj = malloc(sizeof(RefCounted)); obj->refcount = 1; return obj; } void refcounted_destroy(RefCounted *obj) { free(obj); } void addref(RefCounted *obj) { ++(obj->refcount); } void release(RefCounted *obj) { --(obj->refcount); if (obj->refcount==0) { refcounted_destroy(obj); } } int main() { RefCounted *obj = refcounted_new(); // Hier KEIN addref(obj) blablabla_irgendwas_mit_obj_machen(obj); release(obj); } |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Das stimmt nicht, denn ein Objekt hat keine Interfacereferenzen.
Deshalb lässt sich das Problem nicht so einfach lösen. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
Wie gesagt, das Problem lässt sich sofort lösen, wenn der Compiler bei Übergabe von irgendwas an einen Const-Parameter eine Variable zwischenschaltet. Immer wenn es da implizit von TObject zu Interface castet. Bzw. bei
Delphi-Quellcode:
gibt es doch dieses Problem.
TestFunction(TSomething.Irgendwas.Create)
Bei
Delphi-Quellcode:
geht es, da Delphi aus diesem gemanagten Result einen Var-Parameter macht, welcher über eine generierte lokale Variable läuft.
TestFunction(Something.GetInterface)
Und bei
Delphi-Quellcode:
würde ich jetzt das selbe Problem vermuten, aber hier ist man selber Schuld, da man Objekt- und gezählte Interfacereferenzen niemals vermischen sollte.
TestFunction(Something.GetObject)
Delphi-Quellcode:
procedure TestFunction(const Reference: IInterface);
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Zitat:
|
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Entschuldigt bitte, daß ich dieses Thema reanimiere, aber ich fand keine Antwort auf meine Frage, und diese Diskussion paßt am besten.
In der Delphi-Hilfe steht, daß der Compiler bei (der Verwendung von) Konstantenpararametern den Code bei strukturierten und String-Parametern optimieren könne. Nur, was bedeutet "strukturiert"? Vermutlich bis wahrscheinlich das, was in C-Sprachen mit/als "struct" deklariert/definiert wird, also Records. Lange Rede, zählen Arrays (die ja ebenfalls Daten strukturiert enthalten) auch zum Const-Optimierungspotential? Ich vermute, ja. |
AW: Code Optimisation: Benutzung von const in prozedur-Köpfen
Jupp, mit strukturierten Typen sind Records gemeint,
also im Prinzip geht es hier um alle Typen, die größer als SizeOf(Pointer) sind. Strings (LongStrings) und dynamische Arrays sind praktisch das Gleiche. (beim String sind noch paar versteckte Felder mit drin und bei dynamischen Arrays fehlt das CopyOnWrite, welches die StringTypen aber haben) Ausnahme sind ShortStrings, welche ein statisches Array sind, also quasi ein Record. Zweite Ausnahme ist der WideString, welches eine Kapselung des BSTR ( ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:14 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