Wednesday 1 February 2012

C# StringBuilder Optimization Tip

In this tip, you want to see how you can optimize the StringBuilder type's Append method in certain cases that deal with short strings. Instead of appending a string literal that contains two characters, you can append two characters separately. Here, we describe why this optimization works and when to apply it.

Test program

This program benchmark harness demonstrates how you can express a two-character append operation in two different ways. In the first tight loop, two space characters are appended in separate method calls; in the second tight loop, a two-character string is appended in a single method call. The first loop has better performance despite being longer to type into code.
Program that tests StringBuilder optimization tip [C#]

using System;
using System.Diagnostics;
using System.Text;

class Program
{
    const int _max = 10000000;
    static void Main()
    {
 StringBuilder builder = new StringBuilder();
 var s1 = Stopwatch.StartNew();
 for (int i = 0; i < _max; i++)
 {
     // Append two characters.
     builder.Append(' ');
     builder.Append(' ');
 }
 s1.Stop();
 builder = new StringBuilder();
 var s2 = Stopwatch.StartNew();
 for (int i = 0; i < _max; i++)
 {
     // Append two characters combined into a string.
     builder.Append("  ");
 }
 s2.Stop();
 Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000 * 1000) /
     _max).ToString("0.00 ns"));
 Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000 * 1000) /
     _max).ToString("0.00 ns"));
 Console.Read();
    }
}

Output

12.99 ns
15.29 ns

Why does this work? The reason appending two characters separately to a StringBuilder is faster is because the string type in the C# language has some complexities that characters do not have. A char type can be represented by a small integer, while a string type must be allocated on the managed heap and have its own memory location.
The string type is a full-fledged object while a char is a number only. To resolve the string argument in Append, the StringBuilder must reference all the characters separately. However, the control path for the char overload is more direct and therefore faster.

When to use char appends? At some point, using separate character appends will slow down the method you are writing. My testing showed that using chars for 1, 2, 3 and often 4 character long strings was more efficient. Longer than that and the strings were more efficient. This may involve locality of reference or simply better string copying routines, such as those that unroll loops and use unsafe code.
What about Response.Write? The Response.Write method, which is often used in ASP.NET, can use this same optimization and the results are sometimes more pronounced. I tested Response.Write and found that single-character writes were faster in sequences of two than two-character string writes.

Summary

In this tip, we looked at one way you can perform micro-optimizations on the StringBuilder type in the C# programming language. While this is not a dramatic performance boost in many cases where StringBuilder is used, it can provide a measurable speedup to much StringBuilder code as well as Response.Write code.

No comments:

Post a Comment