Performance Tips: System.Text.StringBuilder and String.Concat

Much has been written about the benefits of using StringBuilder over string concatenation. So much so that there seems to be overuse of StringBuilder class, causing performance issues again.

If you're using StringBuilder and string concatenation a lot in your program, here are a few more things to ponder:

  1. String.Concat(str1, str2) is a perfect string concatenation function for two strings, so do not use StringBuilder for two strings.
  2. String.Concat(str1, str2, str3) and String.Concat(str1, str2, str3, str4) are almost perfect for concatenating three or four strings. Do not use StringBuiider for those cases.
  3. Even if your code has complicated conditions to determine the strings to concatenate, as long as they're no more than 4 strings, it still pays to use string addition or String.Concat. You can have empty strings in the expression.
  4. If you're concatenate an array of strings, String.Join(string separator, string[] value, int startIndex, int count) is the perfect method for them.
  5. String Concat works best when all operands are string objects, non-objects will be boxed, non strings will be converted to String using ToString() method. Try to avoid such conversions. For example, path + '\\' + filename should be converted to path + "\\" + filename.
  6. Do not use StringBuilder.Append(expr1 + expr2 + expr3), use Append(expr1).Append(expr2).Append(expr3) instead.
  7. StringBuilder.ToString always generate a new string, be aware of possible string duplications. As comparison, String.Concat(str1, str2) does not generate a new string if either operand is empty.
  8. When StringBuilder is out of memory, it allocates a new array of its current size, thus doubling its capacity. So on average, 25% space is wasted unless you specify the right size for it.
  9. String.Format is implemented using StringBuilder.AppendFormat. Do not use it for simple formatting expressions like "{0}.{1}". It's much better to use expr1 + "." + expr2. Exception to this rule: the format strings need to be put into a resource code for things like customization/localization.
  10. If StringBuilder is used often, consider recycle it for reuse, instead of re-creating them everytime. Be careful about possible threading issues. This is one of the optimization implemented in .Net 4.5.

Examples:

Bad

    String.Format("{0}.{1}", DeclaringType.FullName, Name)

Better

    DeclaringType.FullName + "." + Name

Bad:

                StringBuilder sb = new StringBuilder();

bool first = true;

if (GetFlag((int) PolicyStatementAttribute.Exclusive ))
{
sb.Append( "Exclusive" );
first = false;
}
if (GetFlag((int) PolicyStatementAttribute.LevelFinal ))
{
if (!first)
sb.Append( " " );
sb.Append( "LevelFinal" );
}

return sb.ToString();

This version is more costly for several reasons:

  1. StringBuilder object is created.
  2. Strings are copied when calling StringBuilder.Append
  3. No size is given, so a smaller buffer is allocated automatically and then new allocations are made during Append calls.
  4. A new string is always generated, even if it's just either "Exclusive" or "LevelFinal".

Better: if you use string + operator, there will be no StringBuilder object, no extra copy, no new buffer allocation, and best of all no new string is generated if it's just a single word.

                String p1 = null;
String p2 = null;
String p3 = null;

if (GetFlag((int) PolicyStatementAttribute.Exclusive))
{
p1 = "Exclusive";
}

if (GetFlag((int) PolicyStatementAttribute.LevelFinal))
{
if (p1 != null)
{
p2 = " ";
}

p3 = "LevelFinal";
}

return p1 + p2 + p3;