Efficient String Concatenation in Delphi

Previous: Looking Innocent

Looking Stupid

A simple way of avoiding the implicit variable is to use the one variable you have, Result:

function Apples(nb : Integer) : String;
begin
   Result := IntToStr(nb);
   Result := Result + ' apple(s)';
end;

While that does look stupid, if you look behind the code at asm, it is much simpler now:;

Project65.dpr.31: begin
005D82B0 53               push ebx
005D82B1 56               push esi
005D82B2 8BDA             mov ebx,edx
005D82B4 8BF0             mov esi,eax
Project65.dpr.32: Result := IntToStr(nb);
005D82B6 8BD3             mov edx,ebx
005D82B8 8BC6             mov eax,esi
005D82BA E8D15FE4FF       call IntToStr
Project65.dpr.33: Result := Result + ' apple(s)';
005D82BF 8BC3             mov eax,ebx
005D82C1 BADC825D00       mov edx,$005d82dc
005D82C6 E819F9E2FF       call @UStrCat
Project65.dpr.34: end;
005D82CB 5E               pop esi
005D82CC 5B               pop ebx

And it executes about twice faster as well, and as a bonus, it will be even faster in multi-threaded applications due to reduced pressure on the memory-manager.

And if you inline the functions, the second one will still keep its advantage, as inlining will only move the implicit variable and frame to the function where the inlining takes place.

Not so stupid after all

Note that this “optimization” use of an explicit local variable can be leveraged with all reference-counted types (dynamic arrays, interfaces, objects in ARC compilers…), and it can also help with debugging, as it makes it very simple to inspect the intermediate returned values.

So sometimes, it can pay twice to write a little more code: it’ll be easier to debug/maintain, and it will run faster.

That was for a single concatenation. What happens if you have many?

Check the followup article: Efficient String Building in Delphi.

5 thoughts on “Efficient String Concatenation in Delphi

  1. Hello Eric!

    Does DWScript have functions that avoid UStrAsg, LStrAsg etc. which are toxic for multithreaded applications?

  2. Interesting, but this seems like premature optimization and reducing the readability of code just to make a string concatenation faster. How fast is the string concatenation now? It’s hard to imagine a single concatenation becoming a bottleneck.

  3. With D7 there is almost no difference between the two methods.

    But… this is faster.

    function ApplesFormatStr(nb : Integer) : String;
    begin
    Result := Format(‘%d apple(s)’,[nb]);
    end;

  4. @sam: IntToStr was just picked as an example of a simple function returning a string. Under D7 you should see a difference as well (did you test with FastMM or the default D7 memory manager?)

    @joseph: Of course you should check with a profiler first 😉
    However for readability, this is not so clear-cut, in a more real-world example with longer strings, longer functions names, etc. you’re bound to need line breaks as well, so length-wise, it’ll be similar.
    Yet the longer form with explicit assignment and step-by-step concatenation will be more easily debugable and maintainable (more break point locations, ability to inspect intermediate steps easily, more detailed feedback for crash call stacks, etc.).
    IMHO it’s one of those case where compact code isn’t really more readable, but is definitely less maintainable and less efficient to boot.

Comments are closed.