95

I understand the benefits of StringBuilder.

But if I want to concatenate 2 strings, then I assume that it is better (faster) to do it without StringBuilder. Is this correct?

At what point (number of strings) does it become better to use StringBuilder?

gkrogers
  • 8,126
  • 3
  • 29
  • 36
Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252

12 Answers12

89

I warmly suggest you to read The Sad Tragedy of Micro-Optimization Theater, by Jeff Atwood.

It treats Simple Concatenation vs. StringBuilder vs. other methods.

Now, if you want to see some numbers and graphs, follow the link ;)

Dawnkeeper
  • 2,844
  • 1
  • 25
  • 41
Alex Bagnolini
  • 21,990
  • 3
  • 41
  • 41
  • +1 for _yes_! Time spent worrying about this is time spent not doing something that might actually matter. – Greg D Dec 01 '09 at 12:49
  • 8
    Your reading is wrong however : it doesn't matter in a lot of cases, when no looping is involved, in other cases however it can matter A LOT – Peter Dec 01 '09 at 13:27
  • 1
    I have removed the edit, because it was just wrong info in an accepted answer. – Peter Dec 01 '09 at 13:32
  • 2
    and just to show how mucht it matters, from the article you are refering to : "In most garbage collected languages, strings are immutable: when you add two strings, the contents of both are copied. As you keep adding to result in this loop, more and more memory is allocated each time. This leads directly to awful quadradic n2 performance" – Peter Dec 01 '09 at 13:37
  • I can't fully agree on that link...he's using the mechanism within a method, just comparing what's faster with _some_ concatenation and what's faster to instance. If you declare the StringBuilder outside of the loop, it clearly beats everything. – Bobby Dec 01 '09 at 13:50
  • I tried using stringbuilder, but then the issue is that I still have to call to string in order to compare it to strings elsewhere in code, which seems to make it more inefficient – James Joshua Street Feb 25 '15 at 19:53
  • 4
    Why is this the accepted answer. I don't think simply dropping a link and saying "go read this" is a good answer – Kellen Stuart Dec 20 '19 at 21:03
53

But if I want to concatinate 2 strings, then I assume that it is better (faster) to do it without StringBuilder. Is this correct?

That is indeed correct, you can find why exactly explained very well on :

Article about strings and StringBuilder

Summed up : if you can concatinate strings in one go like

var result = a + " " + b  + " " + c + ..

you are better off without StringBuilder for only on copy is made (the length of the resulting string is calculated beforehand.);

For structure like

var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

new objects are created each time, so there you should consider StringBuilder.

At the end the article sums up these rules of thumb :

Rules Of Thumb

So, when should you use StringBuilder, and when should you use the string concatenation operators?

  • Definitely use StringBuilder when you're concatenating in a non-trivial loop - especially if you don't know for sure (at compile time) how many iterations you'll make through the loop. For example, reading a file a character at a time, building up a string as you go using the += operator is potentially performance suicide.
  • Definitely use the concatenation operator when you can (readably) specify everything which needs to be concatenated in one statement. (If you have an array of things to concatenate, consider calling String.Concat explicitly - or String.Join if you need a delimiter.)

  • Don't be afraid to break literals up into several concatenated bits - the result will be the same. You can aid readability by breaking a long literal into several lines, for instance, with no harm to performance.

  • If you need the intermediate results of the concatenation for something other than feeding the next iteration of concatenation, StringBuilder isn't going to help you. For instance, if you build up a full name from a first name and a last name, and then add a third piece of information (the nickname, maybe) to the end, you'll only benefit from using StringBuilder if you don't need the (first name + last name) string for other purpose (as we do in the example which creates a Person object).

  • If you just have a few concatenations to do, and you really want to do them in separate statements, it doesn't really matter which way you go. Which way is more efficient will depend on the number of concatenations the sizes of string involved, and what order they're concatenated in. If you really believe that piece of code to be a performance bottleneck, profile or benchmark it both ways.

Gleb S
  • 152
  • 2
  • 13
Peter
  • 47,963
  • 46
  • 132
  • 181
17

System.String is an immutable object - it means that whenever you modify its content it will allocate a new string and this takes time (and memory?). Using StringBuilder you modify the actual content of the object without allocating a new one.

So use StringBuilder when you need to do many modifications on the string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Victor Hurdugaci
  • 28,177
  • 5
  • 87
  • 103
9

Not really...you should use StringBuilder if you concatenate large strings or you have many concatenations, like in a loop.

Bobby
  • 11,419
  • 5
  • 44
  • 69
  • 1
    That is wrong. You should use `StringBuilder` only if the loop or the concatenation is a performance problem to the specs. – Alex Bagnolini Dec 01 '09 at 12:23
  • -1 for incorrect generalisation. string s = "a" + "b" + "c" + "d"; uses one memory allocation for s. Look at the last (9th) overload for string.Concat. The above code basically gets compiled down to string.Concat("a", "b", "c", "d") in MSIL. – Binary Worrier Dec 01 '09 at 12:43
  • 2
    @Alex: Isn't that always the case? ;) No, seriously, I've always used StringBuilder for concatenation inside a loop...though, my loops all have more than 1k iterations... @Binary: Normally, that should be compiled to `string s = "abcd"`, at least that's the last thing I heard...though, with variables it would be, most likely, Concat. – Bobby Dec 01 '09 at 12:53
  • 1
    The fact is: it nearly ALWAYS IS NOT the case. I always used string operators `a + "hello" + "somethingelse"` and never had to worry about it. If it will become a problem, I'll use StringBuilder. But I didn't worry about it in the first place, and spent less time writing it. – Alex Bagnolini Dec 01 '09 at 13:04
  • @Bobby: Correct, used literals rather than explain a, b, c, & d are strings :) – Binary Worrier Dec 01 '09 at 13:21
  • 3
    There’s absolutely no performance benefit with large strings – *only* with many concatenations. – Konrad Rudolph Dec 01 '09 at 13:32
  • 1
    @Konrad: Are you sure there's *no* performance benefit? Every time you concatenate large strings, you're copying a large amount of data; Every time you concatenate small strings, you're only copying a small amount of data. – LukeH Dec 01 '09 at 13:46
  • @Konrad: You're most likely right, though, I thought about _really_ large strings...I could imagine that there might be a gain _somewhere_. @Alex: A simple loop (32768 iterations, okay, are a little much, but you get the picture) with adding an 'a' to a String (+=) and to a StringBuilder (.Append()) turns on my machine up with 870ms to 1ms... @Binary: Strings would become the Concat, yes. ;) – Bobby Dec 01 '09 at 13:47
6
  • If you concatenate strings in a loop, you should consider using StringBuilder instead of regular String
  • In case it's single concatenation, you may not see the difference in execution time at all

Here is a simple test app to prove the point:

static void Main(string[] args)
    {
        //warm-up rounds:
        Test(500);
        Test(500);

        //test rounds:
        Test(500);
        Test(1000);
        Test(10000);
        Test(50000);
        Test(100000);

        Console.ReadLine();
    }

    private static void Test(int iterations)
    {
        int testLength = iterations;
        Console.WriteLine($"----{iterations}----");

        //TEST 1 - String
        var startTime = DateTime.Now;
        var resultString = "test string";
        for (var i = 0; i < testLength; i++)
        {
            resultString += i.ToString();
        }
        Console.WriteLine($"STR: {(DateTime.Now - startTime).TotalMilliseconds}");



        //TEST 2 - StringBuilder
        startTime = DateTime.Now;
        var stringBuilder = new StringBuilder("test string");
        for (var i = 0; i < testLength; i++)
        {
            stringBuilder.Append(i.ToString());
        }
        string resultString2 = stringBuilder.ToString();
        Console.WriteLine($"StringBuilder: {(DateTime.Now - startTime).TotalMilliseconds}");


        Console.WriteLine("---------------");
        Console.WriteLine("");

    }

Results (in milliseconds):

----500----
STR: 0.1254
StringBuilder: 0
---------------

----1000----
STR: 2.0232
StringBuilder: 0
---------------

----10000----
STR: 28.9963
StringBuilder: 0.9986
---------------

----50000----
STR: 1019.2592
StringBuilder: 4.0079
---------------

----100000----
STR: 11442.9467
StringBuilder: 10.0363
---------------
5

There's no definitive answer, only rules-of-thumb. My own personal rules go something like this:

  • If concatenating in a loop, always use a StringBuilder.
  • If the strings are large, always use a StringBuilder.
  • If the concatenation code is tidy and readable on the screen then it's probably ok.
    If it isn't, use a StringBuilder.
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • I know this is an old topic, but I am only know learning and wanted to know what you consider to be a "Large string"? – MatthewD Aug 10 '15 at 15:57
5

To paraphrase

Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch

I generally use string builder for any block of code which would result in the concatenation of three or more strings.

Russell Steen
  • 6,494
  • 6
  • 38
  • 56
  • It depends : Concetanation only makes one copy : "Russell" + " " + Steen + ".", will only make one copy because it calcs the length of the string beforehand. Only when you have to split your concatenation, you should begin to think about a builder – Peter Dec 01 '09 at 12:28
5

Since it's difficult to find an explanation for this that's not either influenced by opinions or followed by a battle of prides I thought to write a bit of code on LINQpad to test this myself.

I found that using small sized strings rather than using i.ToString() changes response times (visible in small loops).

The test uses different sequences of iterations to keep time measurements in sensibly comparable ranges.

I'll copy the code at the end so you can try it yourself (results.Charts...Dump() won't work outside LINQPad).

Output (X-Axis: Number of iterations tested, Y-Axis: Time taken in ticks):

Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10 Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10

Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80 Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80

Iterations sequence: 100, 200, 300, 400, 500 Iterations sequence: 100, 200, 300, 400, 500

Code (Written using LINQPad 5):

void Main()
{
    Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
    Test(10, 20, 30, 40, 50, 60, 70, 80);
    Test(100, 200, 300, 400, 500);
}

void Test(params int[] iterationsCounts)
{
    $"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
    
    int testStringLength = 10;
    RandomStringGenerator.Setup(testStringLength);
    var sw = new System.Diagnostics.Stopwatch();
    var results = new Dictionary<int, TimeSpan[]>();
        
    // This call before starting to measure time removes initial overhead from first measurement
    RandomStringGenerator.GetRandomString(); 
        
    foreach (var iterationsCount in iterationsCounts)
    {
        TimeSpan elapsedForString, elapsedForSb;
        
        // string
        sw.Restart();
        var str = string.Empty;

        for (int i = 0; i < iterationsCount; i++)
        {
            str += RandomStringGenerator.GetRandomString();
        }
        
        sw.Stop();
        elapsedForString = sw.Elapsed;


        // string builder
        sw.Restart();
        var sb = new StringBuilder(string.Empty);

        for (int i = 0; i < iterationsCount; i++)
        {
            sb.Append(RandomStringGenerator.GetRandomString());
        }
        
        sw.Stop();
        elapsedForSb = sw.Elapsed;

        results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
    }


    // Results
    results.Chart(r => r.Key)
    .AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
    .AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
    .DumpInline();
}

static class RandomStringGenerator
{
    static Random r;
    static string[] strings;
    
    public static void Setup(int testStringLength)
    {
        r = new Random(DateTime.Now.Millisecond);
        
        strings = new string[10];
        for (int i = 0; i < strings.Length; i++)
        {
            strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
        }
    }
    
    public static string GetRandomString()
    {
        var indx = r.Next(0, strings.Length);
        return strings[indx];
    }
}
Menol
  • 1,230
  • 21
  • 35
4

But if I want to concatenate 2 strings, then I assume that it's better and faster to do so without StringBuilder. Is this correct?

Yes. But more importantly, it is vastly more readable to use a vanilla String in such situations. Using it in a loop, on the other hand, makes sense and can also be as readable as concatenation.

I’d be wary of rules of thumb that cite specific numbers of concatenation as a threshold. Using it in loops (and loops only) is probably just as useful, easier to remember and makes more sense.

surendrapanday
  • 530
  • 3
  • 13
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • "I’d be wary of rules of thumb that cite specific numbers of concatenation as a threshold" < this. Also, after common sense has been applied, think about the person coming back to your code in 6 months time. – Phil Cooper Jul 01 '14 at 09:36
3

A single concatenation is not worth using a StringBuilder. I've typically used 5 concatenations as a rule of thumb.

Nefariis
  • 3,451
  • 10
  • 34
  • 52
Matt Wrock
  • 6,590
  • 29
  • 23
3

As long as you can physically type the number of concatenations (a + b + c ...) it shouldn't make a big difference. N squared (at N = 10) is a 100X slowdown, which shouldn't be too bad.

The big problem is when you are concatenating hundreds of strings. At N=100, you get a 10000X times slowdown. Which is pretty bad.

wisty
  • 6,981
  • 1
  • 30
  • 29
2

I don't think there's a fine line between when to use or when not to. Unless of course someone performed some extensive testings to come out with the golden conditions.

For me, I will not use StringBuilder if just concatenating 2 huge strings. If there's loop with an undeterministic count, I'm likely to, even if the loop might be small counts.

o.k.w
  • 25,490
  • 6
  • 66
  • 63
  • Indeed it would be completely wrong to ues StringBuilder to concat 2 strings, but that is nothing to do with perf. testing - that is simply using it for the wrong thing. – Marc Gravell Dec 01 '09 at 15:31
  • @MarcGravell but isn't the whole point of StringBuilder to increase performance? I thought using StringBuilder for two strings only doesn't make sense because there's more work involved creating the StringBuilder object and the GC required etc compared to just concatenating two strings using +. – David Klempfner Oct 26 '22 at 23:40
  • 1
    @DavidKlempfner it is there to *decrease* the impact of generating lots and lots of intermediate strings; that's *how* it improved performance. If you're only looking to concatenate once: *that isn't an issue* - there are no unnecessary intermediate strings, and no unnecessary work to avoid – Marc Gravell Oct 27 '22 at 06:06
  • @MarcGravell If concatenating two strings, it is better to use + over StringBuilder, not only because StringBuilder wasn't designed for that, but also because using + doesn't involve creating a StringBuilder object on the heap? – David Klempfner Oct 27 '22 at 10:51
  • @DavidKlempfner yes; the `StringBuilder` adds nothing useful for that scenario – Marc Gravell Oct 27 '22 at 10:54