How often have you been told to use StringBuilder to concatenate strings in .NET? My guess is often enough. Here is something you may not know about string concatenation: StringBuilder is not always faster. There are already many articles out there that explain the why’s, I am not going to do that here. But I do have some test data for you.
This block of code took 1484 milliseconds to run on my PC:
for (int i = 0; i <= 1000000; i++) { // Concat strings 3 times using StringBuilder StringBuilder s = new StringBuilder(); s.Append(i.ToString()); s.Append(i.ToString()); s.Append(i.ToString()); }
And this one, using traditional concatenation, took slightly less time (1344 milliseconds):
for (int i = 0; i <= 1000000; i++) { // Concat strings 3 times using traditional concatenation string s = i.ToString(); s = s + i.ToString(); s = s + i.ToString(); }
The above data suggests that StringBuilder only starts to work faster once the number of concatenations exceed 3.
When building a large string from several string literals (such as building a SQL block, or a client side javascript block), use neither traditional concatenation nor StringBuilder. Instead, choose one of the methods below:
// Build script block string s = "<script>" + "function test() {" + " alert('this is a test');" + " return 0;" + "}";
The compiler concatenates that at compile time. At run-time, that works as fast as a big string literal.
I sometimes use the @ string literal which allows for newlines (I find this syntax is harder to maintain, formatting-wise, than using the + operator):
string s = @"<script> function test() { alert('this is a test'); return 0; }";
Both methods above run about 40 times faster than using StringBuilder or traditional string concatenation.
I have posted a follow-up article to provide more detailed analysis and to answer some of the questions asked by readers.
To list available contexts: kubectl config get-contexts To show the current context: kubectl config current-context…
kubectl exec -it <podname> -- sh To get a list of running pods in the…
# Create a soft symbolic link from /mnt/original (file or folder) to ~/link ln -s…
git config --global user.name "<your name>" git config --global user.email "<youremail@somewhere.com>" Related Commands Show current…
TypeScript/JavaScript function getLastMonday(d: Date) { let d1 = new Date(d.getFullYear(), d.getMonth() + 1, 0); let…
I had to do some SMTP relay troubleshooting and it wasn't obvious how to view…
View Comments
What about string.Concat() ?
--larsw
I don't find this argument very convincing.
It is well known and pretty obvious that the compiler will concatenate string literals at compile time. But good to point it out again.
But the rule of thumb on three dynamic values seems way too vague. What if each dynamic value results in strings 4,000 characters long? Why 3? Did you compare the above two loops with 4 values or more? What is the IL doing in those loops? Is it possible the JIT is noticing you never use the generated objects and just not creating them at all?
Matt: Good questions and thanks for asking. In my tests (I only included one test in the article), once the number of concats goes to 4, then the StringBuilder operations started to work faster.
I also thought about the possible JIT optimizations but honestly I did not look at what the IL was doing. I purposefully included the dynamic loop variable "i" in the concatenations to make sure the compiler did not "optimize away" the code. However, I will do some analysis into the generated IL for some definite answers.
Lars: I didn't even know about string.Concat before today. It appears to be a better way to concatenate up to four dynamic strings than the "+" operator because it allocates memory for all the values in advance. I hope to shed some more light on string.Concat in a follow-up article.
@Chinh Do: string.Concat is not _better_ than "+", it's identical. If you write "string s= a + b + c;" the compiler will actually emit "string s= string.Concat(a,b,c);". It has to be one statement though, which is one reason why your sample loop didn't use it.
Have a look at http://ajdotnet.wordpress.com/2007/05/20/about-the-virtue-of-not-improving-performance/ for details.
Regarding the "stringbuilder in loop" example: There may be cases when you cannot avoid such code. It may however be improved by allocating the stringbuilder before the loop and reusing it.
HIH,
AJ.NET
Interesting article! I've done some research on string performance this week as well, in case you're interested: http://blog.cumps.be/string-concatenation-vs-memory-allocation/
What about String.Format("{0}{1}",string1,string2);
?
AJ.NET: Thanks for the info re string.Concat. That's nice to know.
Re the "for" loop, that's there intentionally and it's not part of what I wanted to measure... it's there to measure multiple times for a more accurate final result. If I didn't have the for loop, the operation would probably too fast to measure accurately.
Flyswat: I have not looked at string.Format vs "+". I hope to experiment some and will post back here with the results.
Hello all, I have posted a follow-up article to provide more detailed analysis and to answer some of the questions asked by readers.
If .Net handles String and StringBuilder like Java does, the reason that 3 is the break even point has to do with object creation. With 3 concatenations, each method creates 5 objects (by my count).
StringBuilder:
1 StringBuilder
3 Strings for input
1 String for the final result
Concatenation:
3 Strings for input
1 String for the result of the concatenation of String 2 + 3
1 String for the result of the concatenation of String 1 and the above