Ayende @ Rahien

It's a girl

The truth about string concatenation performance...

Here is a riddle, what is faster?

  • string str = "Id: " + i;
  • string str = string.Format("Id: {0}", i);
  • string str = new StringBuilder().Append("Id: ").Append(i).ToString();

If you guess StringBuilder or string.Format, you are mistaken. Over 10 million iterations, the simple "Id: " + i finished in 4.7 seconds, StringBuilder in 5.7 seconds and string.Format in 7.6 seconds.

The reason for that is that the compiler can optimize the + operator to a call to string.Concat, and it does it quite often when you have several parameters. The optimizations of StringBuilder only shows up if you have several concatenations, or if you are using it on more than a single expression.

Comments

Tom Opgenorth
07/05/2007 10:53 PM by
Tom Opgenorth

The rule of thumb that I use is that if you're just concatenting two or three strings, just use the '+' operator or String.Concat.

String.Format, while slow, sure is handy.

Kevin Dente
07/06/2007 12:02 AM by
Kevin Dente

Back in the 1.1 days, it was up 4 values that would be optimized to a call to Concat:

http://weblogs.asp.net/kdente/archive/2003/08/27/25579.aspx

Don't know if that changed for 2.0 though.

Either which way, mostly a micro-optimization, though. ;)

James Kovacs
07/06/2007 03:59 AM by
James Kovacs

Honestly this isn't too surprising if you think about what is being done. In the first instance, it's a simple string concat, as Oren mentions. For #2, you need to parse out the {0}. I find it amazing that the perf is the same order of magnitude. For #3, you have to allocate a StringBuilder object. As I recall, the break-even for StringBuilder is about a dozen concatentations. Fewer than that and your perf is dominated by allocation of the StringBuilder object. One advantage of StringBuilder is that it reduces the number of temporary strings that have to be allocated as it maintains an internal buffer, which is grown on an as-needed basis. Remember that premature optimization is the root of all evil. Fast enough and maintainable is better than blazingly fast and cryptic, IMHO.

Chris May
07/06/2007 04:23 AM by
Chris May

I think I remember reading that somewhere around 4-8 concats it becomes more performant to use the stringbuilder.

Here is an article talking about it:

http://www.heikniemi.net/hc/archives/000124.html

Damien Guard
07/06/2007 06:37 AM by
Damien Guard

The point of StringBuilder is that it's much faster when dealing with large blocks of text.

[)amien

Grimace of Despair
07/06/2007 07:23 AM by
Grimace of Despair

Ha... by default, Resharper suggests string.format rewrites over concatenations.

If only the aspnet compiler or some preprocessor would translate string.formats into concatenations :P

Tommaso Caldarola
07/06/2007 07:26 AM by
Tommaso Caldarola

Make no sense to have

string str = new StringBuilder()

in the iteration. Usually at the end of the loop you call the ToString() of the StringBuilder object.

Sendhil
07/06/2007 08:34 AM by
Sendhil

"...The optimizations of StringBuilder only shows up if you have several concatenations..." Right!

To elaborate:

You need a new string every iteration in the loop. As 'Tommaso Caldarola' states, StringBuilder makes more sense if you want to have one string built over the whole loop.

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 10000000; i++)

        {                

            sb.Append("Id: ").Append(i).Append(",");

        }

        str = sb.ToString();

And you can see the real difference! I think this loop will take ages to run for a string concatenation.

Sendhil

Rik Hemsley
07/06/2007 08:47 AM by
Rik Hemsley

Good old StringBuilder and its inbuilt idiot detector.

If you hear someone telling you how your concatenation of three strings should be done using StringBuilder, you know instantly they're an idiot.

Stefan Wenig
07/06/2007 11:31 AM by
Stefan Wenig

The Concat method can (and will) determine the size of the resulting string in advance, so only one allocation needs to be done. (They add one alloc by copying the whole array into another array where null gets replaced by String.Empty though.)

StringBuilder has no advance knowledge of the required result buffer size and has to reallocate each time the buffer size is exceeded. Without measuring, I don't see why StringBuilder should have better performance compared to String.Concat for larger sizes. I guess unless you guess the buffer size right and pass it to the StringBuilder ctor, the opposite should be true.

I'm also surprised that String.Format plays in the same league. I think I tested it myself once and found it to be really slow.

Oren: I don't see why String.Concat(s1,s2) should be any faster than String.opAddition(s1,s2) (except that opAddition is not defined for String). Performance advantages start with 3 arguments, i.e. String.Concat(s1,s2,s3) instead of String.opAddition (String.opAddition(s1,s2),s3).

BTW, String.Concat gets called for any number of arguments, I just tested it.

oren:

Adam Machanic
07/06/2007 02:02 PM by
Adam Machanic

It's the KISS principle in action. Use StringBuilder in a loop or somewhere that makes sense. Use + when you just need something quick and simple. Don't try to needlessly complicate your code for the sake of performance... Why, oh why, do certain devs think that maintainability should be sacrificed for a tiny percentage of a performance gain (which, as this post points out, is usually a net loss)?

Jon Skeet
07/09/2007 03:53 PM by
Jon Skeet

I vote for option 4:

string str = "Id: " + i.ToString();

That way it calls the Concat(string[]) method instead of Concat(object[]).This means:

1) No need to call ToString on "Id: "

2) No boxing

It only saves about 5% over the 10 million iterations, but if we're microbenchmarking we might as well go for all we can :)

Jon

Comments have been closed on this topic.