Why concatenating strings in multiple lines faster than Adding String In One Statement?

Why the speed rank is: AddStringInMutipleStatement > AddStringInOneStatement > AddStringInMutipleStatementEx ?

Is it related the temp string object created on runtime? What the detail is?

namespace Test
{
    class Program
    {
        static string AddStringInOneStatement(int a, string b, int c, string d)
        {
            string str;
            str = "a = " + a.ToString() + "b = " + b + "c = " + c.ToString() + "d = " + d; 
            return str;
        }

        static string AddStringInMutipleStatement(int a, string b, int c, string d)
        {
            string str = "";
            str += "a = " + a.ToString();
            str += "b = " + b;
            str += "c = " + c.ToString();
            str += "d = " + d;
            return str;
        }

        static string AddStringInMutipleStatementEx(int a, string b, int c, string d)
        {
            string str = "";
            str += "a = ";
            str += a.ToString();
            str += "b = ";
            str += b;
            str += "c = ";
            str += c.ToString();
            str += "d = ";
            str += d;
            return str;
        }

        static void Main(string[] args)
        {
            uint times = 10000000;
            Stopwatch timer = new Stopwatch();
            timer.Start();
            for (int i = 0; i < times; i++)
            {
                AddStringInOneStatement(1, "2", 3, "4");
            }
            timer.Stop();
            Console.WriteLine("First: " + timer.ElapsedMilliseconds); // 4341 ms
            timer.Reset();
            timer.Start();
            for (int i = 0; i < times; i++)
            {
                AddStringInMutipleStatement(1, "2", 3, "4");
            }
            timer.Stop();
            Console.WriteLine("Second: " + timer.ElapsedMilliseconds); // 3427 ms
            timer.Reset();
            timer.Start();
            for (int i = 0; i < times; i++)
            {
                AddStringInMutipleStatementEx(1, "2", 3, "4");
            }
            timer.Stop();
            Console.WriteLine("Second: " + timer.ElapsedMilliseconds); // 5701 ms
        }
    }
}
Jon Skeet
people
quotationmark

Firstly, a few points on your benchmarking:

  • I would suggest adding a call to each method before the timing starts, so that you're not measuring JIT compilation time
  • I would suggest adding a call to GC.Collect() after each test, so that garbage created by one test doesn't affect another
  • You may get significantly different results on x64 vs x86

There are various costs here:

  • Method call overhead
  • String creation
  • Array creation (hidden)
  • Integer to string conversion

In the first method, the compiler is actually transforming your code into:

string[] bits = new string[] { "a = ", a.ToString(),
                               "b = ", b,
                               "c = ", c.ToString(),
                               "d = ", d };
return string.Concat(bits);

The second and third methods create several intermediate strings; it looks like they create roughly the same number of intermediates (because each line in the second method creates two) but the third method requires more copying - the intermediate strings all contain "the whole of the string so far" whereas half of the intermediate strings in the second method are just short ("b = " + b etc). That may be responsible for the difference.

I suspect in this case the cost of the array creation (and population) in the first method outweighs the cost of the intermediate strings in the second method. Indeed, changing the first method to just create and population the array, that appears to take up more than half of the running time (on my machine). That creation/population includes the int.ToString() calls as well, mind you... which seem to be a large part of the expense too.

I've removed the int.ToString() part in a local copy of your benchmark, and the result still holds - but is even clearer as there's less constant overhead.

people

See more on this question at Stackoverflow