There are a lot of new things to learn with Delphi’s new Unicode string type(s). For example look at Lars’ blog. Quite some interesting posts over the last days.

Apparently, the new Delphi 2009 TStringBuilder class has not got much attention yet, so I’ll give a short recap here.


Under .NET there is a StringBuilder class since the very beginning. This is because in .NET Strings are immutable, which means that once the content of a string instance is set, it is fixed. It cannot be changed anymore. “Quasi modifying” operations require new instances to be created and the old ones to be discarded:

s := s + ‘a’  means “create a new string instance and initialize it with the old content of s plus an ‘a’, then throw the old instance s pointed to into the garbage collector”

Lot of pressure on the memory management system that is.

Thus StringBuilder was invented. The core idea is to prepare some buffer and let all operations work on that buffer, so that you don’t need to recreate instances all the time. The buffer is still managed, and all are happy.

The question is now: “why do we need a TStringBuilder class in Delphi/Win32, and if it is a “real” class (i.e. not just a highly optimized type in System.pas), could it be really fast?”

Well, it appears to be fast, look at this code (test environment: Core 2 Duo, 2GHz):


// approx 2.2 sec ~45M operations/sec
LStart := GetTickCount;
for i := 0 to 100000000 do begin
SB.Append(' ');
end;
LStop := GetTickCount;
Writeln('StringBuilder: ', LStop - LStart);

// approx 3.8 sec ~ 26M operations/sec
s:='';
LStart := GetTickCount;
for i := 0 to 100000000 do begin
s := s + ' ';
end;
LStop := GetTickCount;
Writeln('String: ', LStop - LStart);

// approx 3.5 sec ~ 28M operations/sec
a:='';
LStart := GetTickCount;
for i := 0 to 100000000 do begin
a := a + ' ';
end;
LStop := GetTickCount;
Writeln('AnsiString: ', LStop - LStart);

Download source: StringBenchmark.zip

So obviously TStringBuilder is useful for certain operations, as its significantly faster than String. As expected AnsiString is faster than String, but its still slower than TStringBuilder.

So this leaves to investigate when TStringBuilder has advantages, besides in this fairly artificial case. To be continued …

  • Eric
    You're actually hitting a corner case of TStringBuilder here, in practice it'll be slower, but of speed comparable to String. And you don't need to benchmarks, artificial or not to know that: you just need to look at the implementation.
    TStringBuilder is implemented around a dynamic array, meaning that it's performance is going to be in the same ballpark as Strings, as the VCL internals for growth and data copy are the same. Differences are going to come from the overhead of the respective function calls.

    Incidentally this means String will win (all the time) against TStringBuilder whenever you concat more than one string at once (s:=s+s1+s2+...), because the RTL has a function for that (which will realloc only once), whereas TStringBuilder doesn't.
  • Binis
    I did test this with kind of similar code but with stringchecks set to off and appending bigger strings At it turns out that StringBuilder is slower than normal operations...
  • {$StringChecks OFF} seems to have very little Influence for me. I've added the full DPR file in the post above
  • Strictly speaking, with the stringbuilder, you also need to get the final result inside your benchmark, in order to achieve the same results as the second benchmark.
  • Bruce McGee
    I'm kind of a broken record on this, but I'm happy for the addition for better code compatibility between Delphi in Win32 and .Net. Particularly for library code that is intended to be used everywhere. Even though I'm still stinging a little about losing VCL.Net.

    Another example; TStringBuilder.Replace is about 3 times faster than StringReplace.
  • That replace advantage is indeed interesting.
blog comments powered by Disqus
CodeGear Technology Partner