StringBuilder is just a Cargo Cult (sometimes)
Can you think of top 10 questions in .NET developer interview? I bet one of them is about string builders. Like “how would you concatenate multiple strings?”. With the correct answer “I’d use StringBuilder
. Because, you know, string
is immutable, allocations, blah, blah, blah…”. And if you pretend to be an experienced developer, your code has 0 (zero) concatenations via +
.
Even when it comes to join 2 (3, 4, 5, …) literals or constants, they still use StringBuilder
. Or course, it’s less effective, because such concatenations usually happen during compilation (constants are embedded into code as literals).
But what about regular strings? Should one always use StringBuilder
? I’ve prepared a small benchmark, using BenchMarkDotNet (source code is available on GitHub) that shows that when the number of strings is less than 20, StringBuilder
has no advantage over +
. Even more, when number of items is less that 10, +
has better performance. And this is exactly the most used scenario of string concatenation. If you have more than 10 items, you’d most likely use the loop with StringBuilder
, or String.Join()
or (if you’re crazy badass) IEnumerable.Aggregate()
.
Here are results from tests running on my machine:
1
2
3
4
5
6
7
8
9
BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Processor=Intel Core i7-4720HQ CPU 2.60GHz (Haswell), ProcessorCount=8
Frequency=2533208 Hz, Resolution=394.7564 ns, Timer=TSC
.NET Core SDK=2.1.2
[Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
DefaultJob : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Method | NumberOfItems | Mean | Error | StdDev | Median |
---|---|---|---|---|---|
StringBuilderAction | 2 | 885.5 ns | 7.824 ns | 7.319 ns | 885.3 ns |
StringConcatenationAction | 2 | 770.6 ns | 6.802 ns | 6.362 ns | 771.4 ns |
StringBuilderAction | 5 | 1,785.5 ns | 10.304 ns | 9.639 ns | 1,784.6 ns |
StringConcatenationAction | 5 | 1,674.5 ns | 20.619 ns | 18.278 ns | 1,672.6 ns |
StringBuilderAction | 10 | 3,215.0 ns | 63.250 ns | 62.120 ns | 3,186.8 ns |
StringConcatenationAction | 10 | 3,313.1 ns | 102.415 ns | 85.521 ns | 3,283.2 ns |
StringBuilderAction | 20 | 5,951.5 ns | 48.102 ns | 40.167 ns | 5,937.8 ns |
StringConcatenationAction | 20 | 6,947.7 ns | 34.813 ns | 29.071 ns | 6,940.4 ns |
StringBuilderAction | 50 | 14,997.1 ns | 347.515 ns | 985.841 ns | 14,672.7 ns |
StringConcatenationAction | 50 | 22,794.2 ns | 474.567 ns | 1,291.094 ns | 22,303.9 ns |
StringBuilderAction | 100 | 27,873.3 ns | 481.219 ns | 375.704 ns | 27,864.9 ns |
StringConcatenationAction | 100 | 59,897.8 ns | 1,948.882 ns | 3,091.133 ns | 59,045.8 ns |
There is another, poor man’s benchmark available on dotnetfiddle.net. It might be less accurate, but +
always wins.
Having that we can conclude that using StringBuilder
to just concatenate a couple of strings is nothing but a Cargo Cult. More code and less performance…
Happy coding!