2024, Apr 13th

C# Benchmark: StringBuilder vs. GenericList. Which one is the best?

By eliasdc.dev

I came across discussions claiming that GenericList might outperform StringBuilder in certain scenarios. Intrigued by this, I decided to conduct a benchmark to clarify the matter and learn more about it.

Benchmark setup code

Without spending your time, below is the Benchmark setup code.

I’ve also uploaded it on GitHub: https://github.com/eliasedc/BenchmarkString

C#
using BenchmarkDotNet.Attributes;
using System.Text;

namespace TesteBanchMark
{
    [MemoryDiagnoser]
    public class BenchmarkStringTest
    {
        private readonly string _stringToAppend;

        [Params(10, 1000, 10_000)]
        public int ParameterLength;

        public BenchmarkStringTest()
        {
            _stringToAppend = new string('e', ParameterLength);
        }

        [Benchmark]
        public string ConcatenateStrings_StringBuilder()
        {
            var sb = new StringBuilder();
            for (int i = 0; i < ParameterLength; i++)
            {
                sb.Append("Eliasdc.dev_" + i + " " + _stringToAppend);
            }

            return sb.ToString();
        }

        [Benchmark]
        public string ConcatenateStrings_GenericList()
        {
            var list = new List<string>();
            for (int i = 0; i < ParameterLength; i++)
            {
                list.Add("Eliasdc.dev_" + i + " " + _stringToAppend);
            }
            return string.Join(string.Empty, list);
        }

        [Benchmark]
        public string ConcatenateStrings_string()
        {
            string str = string.Empty;
            for (int i = 0; i < ParameterLength; i++)
            {
                str = str + "Eliasdc.dev_" + i + " " + _stringToAppend;
            }
            return str;
        }

        /////////////////////////////APPENDLINE///////////////////////////////

        [Benchmark]
        public string ConcatenateStrings_StringBuilderAppendLine()
        {
            var sb = new StringBuilder();
            for (int i = 0; i < ParameterLength; i++)
            {
                sb.AppendLine("Eliasdc.dev_" + i + " " + _stringToAppend);
            }

            return sb.ToString();
        }

        [Benchmark]
        public string ConcatenateStrings_GenericListAppendLine()
        {
            var list = new List<string>();
            for (int i = 0; i < ParameterLength; i++)
            {
                list.Add("Eliasdc.dev_" + i + " " + _stringToAppend + "\r\n");
            }
            return string.Join(string.Empty, list);
        }

        [Benchmark]
        public string ConcatenateStrings_stringAppendLine()
        {
            string str = string.Empty;
            for (int i = 0; i < ParameterLength; i++)
            {
                str = str + "Eliasdc.dev_" + i + " " + _stringToAppend + "\r\n";
            }
            return str;
        }
    }
}

YES! Just for fun, I’m also running a test on the native ‘string’ class.

FYI: I also tested the ‘ConcatenateStrings_GenericListAppendLine’ code below, but it didn’t alter the conclusion. I chose to stick with the code described above because it’s more similar to the ‘AppendLine’ function from ‘StringBuilder’.

C#
public string ConcatenateStrings_GenericListAppendLine_v2()
{
    var list = new List<string>();
    for (int i = 0; i < ParameterLength; i++)
    {
        list.Add("Eliasdc.dev_" + i + " " + _stringToAppend);
    }
    return string.Join("\r\n", list);
}

Benchmark Results

According to the results, for small strings, the GenericList showed better performance. However, the simple act of adding a ‘break line’ already tips the scale in favor of StringBuilder. As the length of the string increases, it becomes increasingly evident that StringBuilder outperforms GenericList. An interesting observation is regarding memory allocation: when not using ‘break lines’, GenericList consumes slightly less memory than StringBuilder.

Conclusion

Given these results, the StringBuilder remains the preferred class to use. While the GenericList showed advantages in certain tests without line breaks, the StringBuilder offers a richer set of functions, such as AppendLine() and ToString(), making string manipulation easier.

As for the ‘string’ class, well, let’s just say there’s not much to say… rsrs

That’s it

In this post, we delved into a C# benchmark comparing the performance of StringBuilder and GenericList in string manipulation.

I hope that it will help you.

See you next time!