6

Im maintaining thise code here which often has a pattern like the following:

StringBuilder result = new StringBuilder();

result.Append("{=" + field.Name + "={");

It seems like a waste with a lot of useless object construction when doing it like this and I want to rewrite to this:

result.Append("{=").Append(field.Name).Append("={");

Is it correct that the first version is putting more strain on the GC? Or is there some optimization in the C# compiler with string literals where concatenating string's with string literals does not create temporary objects?

10
  • 6
    The compiler is smart enough to generate a call to one of the String.Concat() overloads. If you are using a StringBuilder for just this concatenation then you're actually worse off. Commented May 26, 2014 at 13:45
  • 1
    Is this all you're using the StringBuilder for? If so, this is wasteful. Just use string concatenation. Commented May 26, 2014 at 13:46
  • @AntP: i guess that OP has left out the loop and he just wants to know if there's a difference between the mix of string concatenation and StringBuilder.Append or a Append only version. Commented May 26, 2014 at 13:53
  • I'm not sure, but I heard that if the number of objects you are concatenating is fixed at compile-time, it is well-optimized and doesn't waste memory. Second, StringBuilder may waste more memory and CPU time than short strings concatenation, so it's preferable to concat few short strings than constructing a StringBuilder for them Commented May 26, 2014 at 13:54
  • 1
    "It. Just. Doesn't. Matter!" (Almost all of the time) blog.codinghorror.com/… Commented May 26, 2014 at 14:53

3 Answers 3

6

I agree with all the answers, but to me you need to understand strings in C# and they way they are actually manipulated 'under the covers'

The use of a StringBuilder comes in to its own when 5 or more strings are being concatenated. This is because the compiler intrinsically converts:

string a = b + c + d + e + f;

into

r = String.Concat(new String[5] { a, b, c, d, e });

so there is an implicit overhead of array creation.

I would suggest reading the following by Eric Lippert who wrote string concatenation in C#:
http://ericlippert.com/2013/06/17/string-concatenation-behind-the-scenes-part-one/ http://ericlippert.com/2013/06/24/string-concatenation-behind-the-scenes-part-two/

Sign up to request clarification or add additional context in comments.

1 Comment

I didnt know that so thanks. Although by looking at the Concat overloads this only happens when concatenating more than 4 strings at once
1

I actually built and ran several tests on this. To get to the results of the test, skip to the bottom. I used this benchmarking method:

public static string BenchmarkMethod(Action method, int iterations)
{
    var watch = new Stopwatch();
    var results = new List<TimeSpan>(iterations);
    for (int iteration = 0; iteration < iterations; iteration++)
    {
    watch.Start();
    method();
    watch.Stop();
    results.Add(watch.Elapsed);
    watch.Reset();
    }

    var builder = new StringBuilder();
    builder.Append("Method benchmarked: ");
    builder.Append(method.Method.ReflectedType);
    builder.Append(".");
    builder.AppendLine(method.Method.Name);
    builder.Append("Average time in ticks: ");
    builder.AppendLine(results.Average(t => t.Ticks).ToString());

    return builder.ToString();
}

I wrote several small methods like these:

public static void StringConcatOperatorX8()
{
    var foo = strings[0] + strings[1] + strings[2] + strings[3] + strings[4] + strings[5] + strings[6] + strings[7] + strings[8];
}

and:

public static void StringBuilderAppendsX8()
{
    var builder = new StringBuilder();
    builder.Append(strings[0]);
    builder.Append(strings[1]);
    builder.Append(strings[2]);
    builder.Append(strings[3]);
    builder.Append(strings[4]);
    builder.Append(strings[5]);
    builder.Append(strings[6]);
    builder.Append(strings[7]);
    builder.Append(strings[8]);

    var result = builder.ToString();
}

Where strings is a string array that contains 9, 30 letter strings

They ranged from 1 to 8 concats/appends. I originally wrote them to go from 1 to 6, using 3 letter strings, and took 10,000 samples.

UPDATE: I have been getting far more samples (1 million to be precise) and adding more letters to the strings. Apparently using StringBuilder is a total waste of performance. At 30 letters using the StringBuilder takes twice as long as using the + operator... At the tests taking several seconds now to complete, I think I shall retire from the subject.

FINAL UPDATE: This is very important as well. The difference in using the + operator and the StringBuilder comes in when you concat on different lines. This method actually takes longer than using the StringBuilder:

public static void StringConcatAltOperatorX8()
{
    var foo = strings[0];
    foo += strings[1];
    foo += strings[2];
    foo += strings[3];
    foo += strings[4];
    foo += strings[5];
    foo += strings[6];
    foo += strings[7];
    foo += strings[8];
}

So at 30 letters per string and 1 million samples, combining all strings into a single string in the same call takes about 5.809297 ticks. Combining all strings in separate lines takes about: 12.933227 ticks. Using the StringBuilder takes 11.27558 ticks. My apologies about the length of the reply. It was something that I needed to check into myself.

1 Comment

Correct, that is the only use case for StringBuilder, when the concatenation needs to happen over multiple lines or in a loop. Otherwise, you just use the binary addition operator.
0

Note that StringBuilder is mutable, unlike String, and is specifically designed for building strings in this manner. Although string concatenation with large strings can easily place noticable strain on the GC, this is typically not a concern if you are using StringBuilder.

If memory consumption is a concern (as it may be with large strings) and you know the approximate length of the final result, you can use the StringBuilder(int) constructor to suggest a start size and minimize memory reallocation as the builder grows.

Note also that you can use AppendFormat to insert a variable into a constant string, e.g.:

result.Append("{=").Append(field.Name).Append("={");

becomes:

result.AppendFormat("{{={0}={{", field.Name);

Which produces exactly the same result, and is largely a matter of preference rather than performance.

Finally, if result is the entire string (rather than part of a larger string being built), simply use String.Format("{{={0}={{", field.Name) or String.Concat("{=", field.Name. "={") rather than StringBuilder. As MSDN notes: "Although the StringBuilder class generally offers better performance than the String class, you should not automatically replace String with StringBuilder whenever you want to manipulate strings. Performance depends on the size of the string, the amount of memory to be allocated for the new string, the system on which your app is executing, and the type of operation. You should be prepared to test your app to determine whether StringBuilder actually offers a significant performance improvement."

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.