Benchmarking, HighResTimer and string concatenation

Got challenged yesterday by a community member. This experienced guy had the following statement:

Just give every object which handles strings a StringBuilder. The overhead is minimal and the performance is maximum.

I do not believe this at all. And although it might be a very micro optimization, it found it worth to try.
For quick output, I’ve used the very handy HighResTimer written by Sitecore(you can find it in Sitecore.Diagnostics). It just returns the ticks which are calculated by the Performance Counters in Windows. So it should give a true answer. I’ve written a small handy wrapper to use it:

    public class Timer : IDisposable
    {
        private static readonly HighResTimer hrt;
        static Timer()
        {
            hrt = new HighResTimer(false);
        }
        public Timer()
        {
            hrt.Start();
        }

        public void Dispose()
        {
            Console.WriteLine(hrt.GetElapsed());
        }
    }

Then it became time to get my test class ready(a console app):

        private const int cycles = 10000000;
        static void Tel(Method m, params string[] input)
        {
            using (new Timer())
            {
                m.Invoke(input);
            }
        }

And write 3 tests:

        static void Concat1(params string[] input)
        {
            var arr = new string[cycles];
            for(int i = 0; i < cycles; i++)
            {
                arr[i] = string.Concat(input);
            }
        }
        static void Concat2(params string[] input)
        {
            var arr = new string[cycles];
            for (int i = 0; i < cycles; i++)
            {
                var sb = new StringBuilder();
                foreach (string s in input)
                {
                    sb.Append(s);
                }
                arr[i] = sb.ToString();
            }
        }
        static void Concat3(params string[] input)
        {
            var arr = new string[cycles];
            for (int i = 0; i < cycles; i++)
            {
                string e = string.Empty;
                foreach (string s in input)
                {
                    e += s;
                }
                arr[i] = e;
            }
        }

So far no fancy stuff. You can doubt about the foreach-loop. But internally string.Concat does the same(it even uses a for-loop following Reflector). And least efficient one, Concat3, should have the biggest disadvantage.
As I know for sure, the StringBuilder is the best one when you’re going to concat more then 10 strings, take a look at these links for examples.
So my test we’re had a maximum reach of 6:

        static void Main()
        {
            string input1 = "aaa";

            Tel(Concat1, input1, input1);
            Tel(Concat2, input1, input1);
            Tel(Concat3, input1, input1);
            Console.WriteLine();

            string input2 = "aaaa";

            Tel(Concat1, input1, input2);
            Tel(Concat2, input1, input2);
            Tel(Concat3, input1, input2);
            Console.WriteLine();

            Tel(Concat1, input2, input2);
            Tel(Concat2, input2, input2);
            Tel(Concat3, input2, input2);
            Console.WriteLine();

            string input3 = "aaaaa";

            Tel(Concat1, input1, input3);
            Tel(Concat2, input1, input3);
            Tel(Concat3, input1, input3);
            Console.WriteLine();

            Tel(Concat1, input2, input3);
            Tel(Concat2, input2, input3);
            Tel(Concat3, input2, input3);
            Console.WriteLine();

            Tel(Concat1, input1, input2, input3);
            Tel(Concat2, input1, input2, input3);
            Tel(Concat3, input1, input2, input3);
            Console.WriteLine();

            Tel(Concat1, input1, input2, input3, input1, input2, input3);
            Tel(Concat2, input1, input2, input3, input1, input2, input3);
            Tel(Concat3, input1, input2, input3, input1, input2, input3);
            Console.WriteLine();
        }

Here are the results:

7374,3959078598
8739,34284944036
2674,68798408736

7556,49502939619
8253,90880684556
3885,96399821765

7379,0495719428
8699,20227291457
2812,4434809452

7774,4316665945
8487,17392853002
2751,89723833616

7969,81627553223
10075,2866635285
3680,81418169069

8520,05939302342
8696,66926941832
7840,39032893845

11077,971082917
19650,061390484
13404,9534228512

Till 4 strings the +-operator is definitely the winner. Above string.Concat goes for gold. But in ALL cases, the StringBuilder was the slowest. So for all you guys, when it comes to concatenation and it are just a couple of strings. Just use string.Concat or the the +-operator. The StringBuilder ruins your code(you’ve to write more code) and it isn’t more efficient!

Guess you may consider me a geek ;). Happy coding :).

For the people who are interested. You can find the class here. Just reference the Sitecore.Kernel for the HighResTimer.

4 thoughts on “Benchmarking, HighResTimer and string concatenation”

  1. Hi Alex,

    This test is unfair! 🙂 Concatenating just two strings is not a job for StringBuilder at all, and at higher numbers += plainly sucks.

    Here are my results: (ms required to perform 1E+6 additions to a string)

    Parts: 2
    StringBuilder: 109.375
    Concat: 109.375
    Concat+Array: 140.625
    +=: 93.75

    Parts: 4
    StringBuilder: 250
    Concat: 203.125
    Concat+Array: 265.625
    +=: 250

    Parts: 8
    StringBuilder: 437.5
    Concat: 359.375
    Concat+Array: 484.375
    +=: 656.25

    Parts: 16
    StringBuilder: 812.5
    Concat: 656.25
    Concat+Array: 937.5
    +=: 1812.5

    Parts: 32
    StringBuilder: 1515.625
    Concat: 1296.875
    Concat+Array: 1843.75
    +=: 5187.5

    Parts: 64
    StringBuilder: 2812.5
    Concat: 2515.625
    Concat+Array: 3453.125
    +=: 16484.375

    Parts: 128
    StringBuilder: 5359.375
    Concat: 4890.625
    Concat+Array: 6703.125
    +=: 57578.125

    You see, already when concatenating 8 strings StringBuilder is better than += it clearly shows O(n) complexity while += shows O(n^2).

    string.Concat itself seems wo be the fastest, but… We need to take into account that string.Concat requires an array of strings, and populating an array takes time too. This sole makes Concat + array operations about 10-20% slower than just StringBuilder.

    So yeah, happy coding, just make sure your += is not inside a loop 😉

    Here is the code I ran to get the results: http://rafb.net/p/0hvZjC71.html

    Regards,
    Dmitry

  2. Advantageously, the post is actually the sweetest topic on this registry related issue. I fit in with your conclusions and will thirstily look forward to your future updates. Just saying thanks will not just be enough, for the extraordinary clarity in your writing. I will immediately grab your rss feed to stay informed of any updates.

Comments are closed.