Delphi; performance of passing const strings versus passing var strings
This is really a comment, but a long one so bear with me.
About 'so called' string passing by value
Delphi always passes string
and ansistring
(WideStrings and ShortStrings excluded) by reference, as a pointer.
So strings are never passed by value.
This can be easily tested by passing 100MB strings around.
As long as you don't change them inside the body of the called routine string passing takes O(1) time (and with a small constant at that)
However when passing a string without var
or const
clause, Delphi does three things.
- Increase the reference count of the string.
- put an implicit try-finally block around the procedure, so the reference count of the string parameter gets decreased again when the method exits.
- When the string gets changed (and only then) Delphi makes a copy of the string, decreases the reference count of the passed string and uses the copy in the rest of the routine.
It fakes apass by value
in doing this.
About passing by reference (pointer)
When the string is passed as a const
or var
, Delphi also passes a reference (pointer), however:
- The reference count of the string does not increase. (tiny, tiny speed increase)
- No implicit try/finally is put around the routine, because it is not needed. This is part 1 why
const/var
string parameters execute faster. - When the string is changed inside the routine, no copy is make the actual string is changed. For
const
parameters the compiler prohibits string alternations. This is part 2 of whyvar/const
string parameters work faster. - If however you need to create a local var to assign the string to; Delphi copies the string :-) and places an implicit try/finally block eliminating 99%+ of the speed gain of a
const
string parameter.
Hope this sheds some light on the issue.
Disclaimer: Most of this info comes from here, here and here
The compiler won't make a copy of the string when using const afaik. Using const saves you the overhead of incrementing/decrementing the refcounter for the string that you use.
You will get a bigger performanceboost by upgrading the memorymanager to FastMM, and, because you do a lot with strings, consider using the FastCode library.
const
for parameters in Delphi essentially means "I'm not going to mutate this, and I also don't care if this is passed by value or by reference - whichever is most efficient is fine by me". The bolded part is important, because it is actually observable. Consider this code:
type TFoo =
record
x: integer;
//dummy: array[1..10] of integer;
end;
procedure Foo(var x1: TFoo; const x2: TFoo);
begin
WriteLn(x1.x);
WriteLn(x2.x);
Inc(x1.x);
WriteLn;
WriteLn(x1.x);
WriteLn(x2.x);
end;
var
x: TFoo;
begin
Foo(x, x);
ReadLn;
end.
The trick here is that we pass the same variable both as var
and as const
, so that our function can mutate via one argument, and see if this affects the other. If you try it with code above, you'll see that incrementing x1.x
inside Foo
doesn't change x2.x
, so x2
was passed by value. But try uncommenting the array declaration in TFoo
, so that its size becomes larger, and running it again - and you'll see how x2.x
now aliases x1.x
, so we have pass-by-reference for x2
now!
To sum it up, const
is always the most efficient way to pass parameter of any type, but you should not make any assumptions about whether you have a copy of the value that was passed by the caller, or a reference to some (potentially mutated by other code that you may call) location.
No, there shouldn't be any performance difference between using const
or var
in your case. In both cases a pointer to the string is passed as the parameter. If the parameter is const
the compiler simply disallows any modifications to it. Note that this does not preclude modifications to the string if you get tricky:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
s := 'foo bar baz';
UniqueString(s);
SetConstCaption(s);
Caption := s;
end;
procedure TForm1.SetConstCaption(const AValue: string);
var
P: PChar;
begin
P := PChar(AValue);
P[3] := '?';
Caption := AValue;
end;
This will actually change the local string variable in the calling method, proof that only a pointer to it is passed.
But definitely use FastMM4, it should have a much bigger performance impact.