12
votes

Quick one; am I right in thinking that passing a string to a method 'as a CONST' involves more overhead than passing a string as a 'VAR'? The compiler will get Delphi to make a copy of the string and then pass the copy, if the string parameter is declared as a CONST, right?

The reason for the question is a bit tedious; we have a legacy Delphi 5 utility whose days are truly numbered (the replacement is under development). It does a large amount of string processing, frequently passing 1-2Kb strings between various functions and procedures. Throughout the code, the 'correct' observation of using CONST or VAR to pass parameters (depending on the job in hand) has been adhered to. We're just looking for a few 'quick wins' that might shave a few microseconds off the execution time, to tide us over until the new version is ready. We thought of changing the memory manager from the default Delphi 5 one to FastMM, and we also wondered if it was worth altering the way the strings are passed around - because the code is working fine with the strings passed as const, we don't see a problem if we changed those declarations to var - the code within that method isn't going to change the string.

But would it really make any difference in real terms? (The program really just does a large amount of processing on these 1kb+ish strings; several hundred strings a minute at peak times). In the re-write these strings are being held in objects/class variables, so they're not really being copied/passed around in the same way at all, but in the legacy code it's very much 'old school' pascal.

Naturally we'll profile an overall run of the program to see what difference we've made but there's no point in actually trying this if we're categorically wrong about how the string-passing works in the first instance!

5

5 Answers

12
votes

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.

9
votes

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.

5
votes

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.

  1. Increase the reference count of the string.
  2. put an implicit try-finally block around the procedure, so the reference count of the string parameter gets decreased again when the method exits.
  3. 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 a pass 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:

  1. The reference count of the string does not increase. (tiny, tiny speed increase)
  2. 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.
  3. 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 why var/const string parameters work faster.
  4. 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

3
votes

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.

2
votes

Const is already the most efficient way of passing parameters to a function. It avoids creating a copy (default, by value) or even passing a pointer (var, by reference).
It is particularly true for strings and was indeed the way to go when computing power was limited and not to be wasted (hence the "old school" label).

IMO, const should have been the default convention, being up to the programmer to change it when really needed to by value or by var. That would have been more in line with the overall safety of Pascal (as in limiting the opportunity of shooting oneself in the foot).

My 2¢...