How to improve StringBuilder performance in C#

In this article, we will learn and discuss How to improve StringBuilder performance in C#. As we know string is an immutable type in C# and .NET. Whenever we modify a string a new string object is allocated into the memory with new string data. StringBuilder is used to address this issue with StringBuilder whenever a string is modified the memory allocation of the string is extended if the string size grows.

Use of StringBuilderCache:

StringBuilderCache is an internal class of .NET and .NET Core. When we have to create multiple instances of StringBuilder StringBuilderCache is used to optimize memory allocation. With StringBuilderCache we can create a single instance of StringBuilder and can reuse it multiple times. StringBuilderCache cache the instance of StringBuilder and this technique can save a lot of memory allocation.

Let’s do this with a code example to understand it. In the below code I have created a method named “DemoStringBuilder()” and created an instance of StringBuilder(). Used the instance to append the string.

public string DemoStringBuilder()
{
    var stringBuilder = new StringBuilder();
    stringBuilder.Append("First Dummy String");
    stringBuilder.Append("Second Dummy String");
    stringBuilder.Append("Third Dummy String");
    return stringBuilder.ToString();
}

In this example, I have used Acquire to cache StringBuilder’s instance and use it to append string. With this method, StringBuilder performance can be optimized.

public string DemoStringBuilderCache()
{
    var stringBuilder = StringBuilderCache.Acquire();
    stringBuilder.Append("First Dummy String");
    stringBuilder.Append("Second Dummy String");
    stringBuilder.Append("Third Dummy String");
    return StringBuilderCache.GetStringAndRelease(stringBuilder);
}

Instead of String.Join use StringBuilder.AppendJoin:

As we all know the string is an immutable type so when we modify string objects it will create new string objects for new data so it consumes a lot of memory allocation. So we should use StringBuilder.AppendJoin instead of String.Join when concatenating the string to improve the code efficiency and performance.

[Benchmark]
            public string StringJoinUse()
            {
                var list = new List<string> {
                        "ASD,
                        "EFG", "HJK", "ZXC", "QWE"
            };
                var stringBuilder = new StringBuilder();
                for (int i = 0; i < 10000; i++)
                {
                    stringBuilder.Append(string.Join(' ', list));
                }
                return stringBuilder.ToString();
            }
            [Benchmark]
            public string AppendJoinUse()
            {
                var list = new List<string> {
                        "ASD,
                        "EFG", "HJK", "ZXC", "QWE"
            };
                var stringBuilder = new StringBuilder();
                for (int i = 0; i < 10000; i++)
                {
                    stringBuilder.AppendJoin(' ', list);
                }
                return stringBuilder.ToString();
            }

Append single Character when needed:

Do not append string using StringBuilder when need to append a single character. Always append a single character with StringBuilder when need to append only a single character. It will put a good impact on the code StringBuilder performance.

[Benchmark]
            public string UseCharAppendString()
            {
                var stringBuilder = new StringBuilder();
                for (int i = 0; i < 1000; i++)
                {
                    stringBuilder.Append('x');
                    stringBuilder.Append('y');
                    stringBuilder.Append('z');
                }
                return stringBuilder.ToString();
            }

Pool of StringBuilder Object:

When we have to use the StringBuilder object multiple times so create StringBuilder objects multiple times so it increases memory allocations drastically. To optimize StringBuilder performance we create StringBuilder object in the objects pool, call the object when needed and return it.

[Benchmark]
            public void UsingPool()
            {
                // Use NuGet package Microsoft.Extensions.ObjectPool
                var objectPoolProvider = new DefaultObjectPoolProvider();
                var sbPool = objectPoolProvider.CreateStringBuilderPool();

                for (var i = 0; i < 10000; i++)
                {
                    var stringBuilder = sbPool.Get();
                    stringBuilder.Append("XYZ");
                    _ = stringBuilder.ToString();
                    sbPool.Return(stringBuilder);
                }
            }

The capacity of StringBuilder:

StringBuilder allows us to set capacity. So if we already know the maximum size of the string to build, we can set it initially. It does not improve the performance as effectively but reduces memory allocations.

[Parameters(1, 1_000, 10_000, 50_000, 999_999, 1_000_000, 1_500_000)]
            public int Size { get; set; }

            [Benchmark]
            public string InitialSize()
            {
                var stringBuilder = new StringBuilder(Size);
                for (int i = 0; i < 1_000_000; i++)
                {
                    stringBuilder.Append('c');
                }
                return stringBuilder.ToString();
            }

Conclusion:

We can improve our StringBuilder performance efficiently by following the above-mentioned points and ways. If you have any suggestions to make this post better or have any queries. Please comment below or you can contact us.

Leave a Reply

Your email address will not be published.