Click here to Skip to main content
15,896,493 members
Articles / Programming Languages / MSIL

String Concatenation vs. Memory Allocation in C# .NET

Rate me:
Please Sign up or sign in to vote.
1.00/5 (2 votes)
21 Dec 2011LGPL32 min read 16.8K   6
String concatenation and a very good performance analysis.

I would like to bring into your attention an old article written on this topic. This article explains a lot about string concatenation and does a very good performance analysis.

Here is a glimpse of this article:

Over the years, plenty has been written about string performance, lots of comparisons between String.Concat and StringBuilder. Today I decided to do some of my own research into the subject and contribute to the knowledge already out there. More specifically, I’ll be taking a look at the memory usage for various concatenation methods and compiler optimizations used to generate the IL.

The test scenario I defined consists of several methods, each returning the same string. The string I created is supposed to resemble a real-life scenario. I identified five different ways of concatenating strings for my test. I will be taking a look at the numbers when calling each method once and inside a very small loop of 50 calls, which is another real-life number in my case.

Single line concatenation.

The easiest way of concatenating strings together is by simply putting a plus sign between them.

C#
public string GetPlussedString()
{
 string myString = "SELECT column1,"
 + " column2,"
 + " column3,"
 + " column4,"
 + " column5,"
 + " column6,"
 + " FROM table1 t1"
 + " JOIN table2 t2"
 + " ON t1.column1 = t2.column1";
 return myString;
}

Although it seems like we are creating 9 string instances, the compiler optimizes this into the following IL:

MSIL
.method public hidebysig instance string GetPlussedString() cil managed
{
 .maxstack 1
 .locals init (
 [0] string myString)
 L_0000: ldstr "SELECT column1, column2, column3, column4, column5, 
         column6, FROM table1 t1 JOIN table2 t2 ON t1.column1 = t2.column1"
 L_0005: stloc.0
 L_0006: ldloc.0
 L_0007: ret
}

In reality, we created one string instance and returned it, which is about the most efficient way we can achieve.

When profiling the test application, I couldn’t even find a call to GetPlussedString in the profiler, which makes me believe the runtime even optimized this.

In total, our application created 113 string instances and barely used any memory.

Running this in the loop gives the following result:

Important to note is the fact that we still have 113 string instances. This is because .NET used String Interning on my string and simply returns a reference to that instance over and over.

You can read the full article here: http://www.cumps.be/nl/blog/commented/string-concatenation-vs-memory-allocation.

Thanks. Hope it helps.

Thanks to the original author for this good article.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior) Siemens
India India
A .net developer since 4+ years, wild, curious and adventurous nerd.

Loves Trekking/Hiking, animals and nature.

A FOSS/Linux maniac by default Wink | ;)

An MVP aspirant and loves blogging -> https://adventurouszen.wordpress.com/

Comments and Discussions

 
GeneralPlease try to understand what others are saying Pin
Hwisung Im14-May-14 19:11
Hwisung Im14-May-14 19:11 
GeneralMy vote of 1 Pin
arvelius14-Mar-13 3:27
arvelius14-Mar-13 3:27 
GeneralMy vote of 1 Pin
Selvin24-Dec-11 4:11
Selvin24-Dec-11 4:11 
GeneralRe: My vote of 1 Pin
zenwalker198525-Dec-11 16:49
zenwalker198525-Dec-11 16:49 
Questionre Pin
pipiscrew22-Dec-11 9:03
pipiscrew22-Dec-11 9:03 
you are crazy,
the sample you provide only declare 1variable as string and the + symbol is to continue the line..

C#
public string GetPlussedString()
{
    string myString0= "SELECT column1,";
    string myString1 =  " column2,";
    string myString2 =  " column3,";
    string myString3 =  " column4,";
    string myString4 =  " column5,";
    string myString5 =  " column6,";
    string myString6 =  " FROM table1 t1";
    string myString7 = " JOIN table2 t2";
    string myString8 =  " ON t1.column1 = t2.column1";


    return myString0 + myString1 +  myString2 + myString3 + myString4 + myString5 + myString6 + myString7 + myString8 ;
}


equal to:

C#
.method public hidebysig instance string GetPlussedString() cil managed
{
    .maxstack 3
    .locals init (
        [0] string V_0,
        [1] string V_1,
        [2] string V_2,
        [3] string V_3,
        [4] string V_4,
        [5] string V_5,
        [6] string V_6,
        [7] string V_7,
        [8] string V_8,
        [9] string[] V_9
    )

    L_0000: ldstr "SELECT column1,"    // 727B000070
    L_0005: stloc.0     // 0A
    L_0006: ldstr " column2,"    // 729B000070
    L_000B: stloc.1     // 0B
    L_000C: ldstr " column3,"    // 72AF000070
    L_0011: stloc.2     // 0C
    L_0012: ldstr " column4,"    // 72C3000070
    L_0017: stloc.3     // 0D
    L_0018: ldstr " column5,"    // 72D7000070
    L_001D: stloc.s V_4    // 1304
    L_001F: ldstr " column6,"    // 72EB000070
    L_0024: stloc.s V_5    // 1305
    L_0026: ldstr " FROM table1 t1"    // 72FF000070
    L_002B: stloc.s V_6    // 1306
    L_002D: ldstr " JOIN table2 t2"    // 721F010070
    L_0032: stloc.s V_7    // 1307
    L_0034: ldstr " ON t1.column1 = t2.column1"    // 723F010070
    L_0039: stloc.s V_8    // 1308
    L_003B: ldc.i4.s 0x9    // 1F09
    L_003D: newarr string    // 8D2D000001
    L_0042: stloc.s V_9    // 1309
    L_0044: ldloc.s V_9    // 1109
    L_0046: ldc.i4.0     // 16
    L_0047: ldloc.0     // 06
    L_0048: stelem.ref     // A2
    L_0049: ldloc.s V_9    // 1109
    L_004B: ldc.i4.1     // 17
    L_004C: ldloc.1     // 07
    L_004D: stelem.ref     // A2
    L_004E: ldloc.s V_9    // 1109
    L_0050: ldc.i4.2     // 18
    L_0051: ldloc.2     // 08
    L_0052: stelem.ref     // A2
    L_0053: ldloc.s V_9    // 1109
    L_0055: ldc.i4.3     // 19
    L_0056: ldloc.3     // 09
    L_0057: stelem.ref     // A2
    L_0058: ldloc.s V_9    // 1109
    L_005A: ldc.i4.4     // 1A
    L_005B: ldloc.s V_4    // 1104
    L_005D: stelem.ref     // A2
    L_005E: ldloc.s V_9    // 1109
    L_0060: ldc.i4.5     // 1B
    L_0061: ldloc.s V_5    // 1105
    L_0063: stelem.ref     // A2
    L_0064: ldloc.s V_9    // 1109
    L_0066: ldc.i4.6     // 1C
    L_0067: ldloc.s V_6    // 1106
    L_0069: stelem.ref     // A2
    L_006A: ldloc.s V_9    // 1109
    L_006C: ldc.i4.7     // 1D
    L_006D: ldloc.s V_7    // 1107
    L_006F: stelem.ref     // A2
    L_0070: ldloc.s V_9    // 1109
    L_0072: ldc.i4.8     // 1E
    L_0073: ldloc.s V_8    // 1108
    L_0075: stelem.ref     // A2
    L_0076: ldloc.s V_9    // 1109
    L_0078: call string string::Concat(string[])    // 283100000A
    L_007D: ret     // 2A

}

AnswerRe: re Pin
zenwalker198522-Dec-11 16:00
zenwalker198522-Dec-11 16:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.