m_d_p29
m_d_p29

Reputation: 163

Which string concatenation operation is faster - "+" or string.Concat

I have been reading different answers on which string concatenation operation is better. I read somewhere that "+" internally calls string.Concat method and string.Concat is faster between both. When I look at the IL code, it doesn't seem to suggest above statements.

For

string s1 = "1" + "2";

IL Code

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr      "12"
  IL_0006:  stloc.0
  IL_0007:  ret
} // end of method Program::Main

Also, I noticed from the IL code that "+" initializes only one string whereas string.Concat initializes two strings to concatenate. I also tried with multiple strings as well. In terms of using the memory, "+" seems to use just one string variable where as the other option uses more variables internally.

For,

string s1 = string.concat("1", "2");

IL Code

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  2
  .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr      "1"
  IL_0006:  ldstr      "2"
  IL_000b:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0010:  stloc.0
  IL_0011:  ret
} // end of method Program::Main

So can we conclude from above IL code that "+" is faster than "string.Concat" as it uses lesser variable for performing the same operation?

Upvotes: 2

Views: 699

Answers (2)

Hamid Pourjam
Hamid Pourjam

Reputation: 20754

There is nothing as Operator + in String class, so the whole + converted to String.Concat is done by C# compiler so they are identical.

Stop doing micro-optimizing or premature-optimization on your code. Try to write a code that performs correctly, then if you face a performance problem later then profile your application and see where is the problem. Strings are immutable. If you have a piece of code which have performance problem due to string concatenation you should consider using StringBuilder.

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%

Upvotes: 6

Theodoros Chatzigiannakis
Theodoros Chatzigiannakis

Reputation: 29213

This comparison is wrong (assuming you are not interested in concatenation of string constants exclusively).

In your first snippet, the concatenation has already been performed by the C# compiler:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr      "12"      // The strings are already concatenated in the IL.
  IL_0006:  stloc.0
  IL_0007:  ret
}

In your second snippet, the call to string.Concat remains:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  2
  .locals init ([0] string s1)
  IL_0000:  nop
  IL_0001:  ldstr      "1"
  IL_0006:  ldstr      "2"
  IL_000b:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0010:  stloc.0
  IL_0011:  ret
}

Therefore, it's meaningless to try to discern the performance of your two snippets using constants, because you'll get non-representative results.

In the general case, the C# compiler will compile a chain of + operators on strings as a single call to string.Concat. You can verify this by performing pretty much the same test that you did, with variables instead of constants.

As a demonstration, consider these two C# methods. One uses + to concatenate strings:

static string Plus(string a, string b, string c)
{
    return a + b + c;
}

The other calls string.Concat:

static string Concat(string a, string b, string c)
{
    return string.Concat(a, b, c);
}

Now look at their respective IL, using a Debug configuration:

.method private hidebysig static string Plus (
        string a,
        string b,
        string c
    ) cil managed 
{
    .locals init (
        [0] string V_0
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: ldarg.2
    IL_0004: call string [mscorlib]System.String::Concat(string,  string,  string)
    IL_0009: stloc.0
    IL_000a: br.s IL_000c

    IL_000c: ldloc.0
    IL_000d: ret
}

And:

.method private hidebysig static string Concat (
        string a,
        string b,
        string c
    ) cil managed 
{
    .locals init (
        [0] string V_0
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: ldarg.2
    IL_0004: call string [mscorlib]System.String::Concat(string,  string,  string)
    IL_0009: stloc.0
    IL_000a: br.s IL_000c

    IL_000c: ldloc.0
    IL_000d: ret
}

They are identical (except for their names). If we build using a Release configuration, we get shorter IL - but still identical for both methods.

In conclusion, in this special case, we can safely assume that we won't observe any performance difference between the two ways of expressing the same thing.

In the general case (where the IL isn't identical or near identical), we can't make any assumptions about performance based on our mental model of the CLR. Even if we do have a completely accurate mental model of the CLR, we must take under consideration that the bytecode ultimately gets compiled machine code, which is different from IL (for example, x86 code has registers but IL doesn't).

To reason about performance we use profilers instead, as they can give us practical, detailed metrics.

Upvotes: 6

Related Questions