C#Dot Net Perls

C#
StringBuilder Mistake and Benchmark

by Sam Allen

Problem

Quickly fix a common StringBuilder mistake. There is a lot of confusion about StringBuilder objects. We want to think about when StringBuilder is better and when it is worse. We want to optimize all of our StringBuilder code that has this mistake.

C# Solution

I have seen code like I show next in the real-world. In fact, some C# code that I installed on this web site for an advertising company made this mistake. Here I will provide some guidance about choosing the best StringBuilder syntax for your programs.

This code is slow

The following code works well--it doesn't have any huge performance problems, and it does what it is supposed to. But I want to show you a way to make it far better. This code could cause a serious performance penalty in some situations.

static void Test1()
{
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < _iterations; i++)
    {
        // Set up our strings we will need to add to the result StringBuilder.
        // They are all modified by adding something to the end, so they are
        // treated as unique strings and not intern ones.
        string extra = i.ToString();
        string before = "okay" + extra;
        string url = "http://sometest.com/" + extra;
        string text = "Some Test" + extra;
        string after = "That's Good" + extra;

        // Build up a new string using + and then pass that to StringBuilder.
        builder.Append(@"<li style=""float: right; clear: none;
display: block; margin: 0; padding: 0;"">" +
            before + @" <a href=""" + url + @""">" + text + "</a> " + after + "</li>");
    }
}

How can we make it faster?

When you use StringBuilder, use only the StringBuilder. In the above example, the programmer combines the string + operator with Append. What this ends up doing is creating a bunch of temporary strings before sending them to the Append method. You can eliminate temporary strings for far better performance.

static void Test2()
{
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < _iterations; i++)
    {
        // Set up the same variables that are in the other example.
        // We make sure they are used as unique strings.
        string extra = i.ToString();
        string before = "okay" + extra;
        string url = "http://sometest.com/" + extra;
        string text = "Some Test" + extra;
        string after = "That's Good" + extra;

        // Chain append calls. Each append returns the StringBuilder, so just
        // append all the variables one-by-one to the result.
        builder.Append(@"<li style=""float: right; clear: none;
display: block; margin: 0; padding: 0;"">").Append(
            before).Append(@" <a href=""").Append(url).Append(@""">").Append(
            text).Append("</a> ").Append(after).Append("</li>");
    }
}

Optimization Results

I gave you the 40% figure already, but let's take a look at some real numbers. This table shows the time for the first function to run 1000 iterations in the inner loop 1000 times. The memory usage isn't big here because the StringBuilder doesn't grow too large in each of the 1000 runs.

Version Time in ms
Before (+) 1841
After (Append) 1154
Results of my StringBuilder optimization.

Conclusion

You should avoid using the + concatenation operator in the same statements as StringBuilder. You can use one or the other, but not both. When I say this is a common mistake, I mean it is one that I have made in the past. I think the best option is to use the syntax in the After example.

Dot Net Perls is dedicated to sharing code and knowledge. It has
© 2007-2008 Sam Allen. All rights reserved.

Ads by The Lounge