tags:

views:

389

answers:

4

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!

+3  A: 

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.

The_Fox
Thanks, The_Fox. We're already using a more-optimised string handling unit (part of HyperString) and from what I gathered via a quick Google, the FastCode Project web page seems to be down/gone, and the notes left on the other main FastCode page imply that Delphi 5 isn't supported. Still, I think I'll mark FastCode as something worth looking into for our Delphi 7 applications if/when the time comes. I'll definitely plug-in FastMM4 though. Thank you!
robsoft
+8  A: 

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.

mghie
Brilliant, thanks mghie. So I was wrong about how const/var worked with strings. Embarrassing, but glad I know now. We're already using HyperString for some of the string manipulation, and I think we'll drop FastMM4 in there and see what that gives us. Cheers!
robsoft
A var string parameter actually passes a pointer to the string variable, while a const string parameter just passes the string value along. Var thus has an extra layer of indirection, so it should be slower than const.
Barry Kelly
?? This doesn't make sense to me. I always thought that (long) strings were always passed by reference (pointer). If marked *const* or *var* then it's a pointer to the passed string, if not marked *either* const *or* var, then a copy is made to avoid side effects if the routine modifies the string parameter and the pointer is to the copy.The only reason I would expect a *var* string param to be only very marginally slower than *const* is that a *var* String param presumably has reference counting in the pragma generated for the call that are not necessary for a *const* string parameter.
Deltics
A: 

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

François
+3  A: 

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.

Pavel Minaev