I have written a small application to test differet way of appending strings
Method 1: Use + operator. Like s = s + " " + str1 + " " + str2 + " " + str3 + " " + str4 + " " + str5
Method 2: Using String.Join. Like s = String.Join(" ", new string [] {s, str1, str2, str3, str4, str5});
Method 3: Using String.Format. Like s = String.Format("{0} {1} {2} {3} {4} {5}", s, str1,str2,str3,str4,str5);
Method 4: Using StringBuilder.
Here is the tick time I got for 5000 iteration:
Method 1: 19438310752
Method 2: 19796152256
Method 3: 48454067568
Method 4: 24874657960
At my surprise the plus operator was faster than StringBuilder method. I am pasting my code here. It would be great anybody can do similar tests and share experiences or check if there is any bug in my code. Here was my code:
using System;
using System.Diagnostics;
using System.Text;
//using System.Threading;
class MySample{
static string test = "abcdefghijklmnopqrstuvwxyz";
static Random rand = new Random();
static string normalAppendTest()
{
string s = "";
for (int i=0; i < 5000; i++)
{
string str1, str2, str3, str4, str5;
str1 = test.Substring(rand.Next(5),20);
str2 = test.Substring(rand.Next(5),20);
str3 = test.Substring(rand.Next(5),20);
str4 = test.Substring(rand.Next(5),20);
str5 = test.Substring(rand.Next(5),20);
s = s + " " + str1 + " " + str2 + " " + str3 + " " + str4 + " " + str5;
}
return s;
}
static string joinAppendTest()
{
string s = "";
for (int i=0; i < 5000; i++)
{
string str1, str2, str3, str4, str5;
str1 = test.Substring(rand.Next(5),20);
str2 = test.Substring(rand.Next(5),20);
str3 = test.Substring(rand.Next(5),20);
str4 = test.Substring(rand.Next(5),20);
str5 = test.Substring(rand.Next(5),20);
s = String.Join(" ", new string[] {s, str1,str2,str3,str4,str5});
}
return s;
}
static string formatAppendTest()
{
string s = "";
for (int i=0; i < 5000; i++)
{
string str1, str2, str3, str4, str5;
str1 = test.Substring(rand.Next(5),20);
str2 = test.Substring(rand.Next(5),20);
str3 = test.Substring(rand.Next(5),20);
str4 = test.Substring(rand.Next(5),20);
str5 = test.Substring(rand.Next(5),20);
s = String.Format("{0} {1} {2} {3} {4} {5}", s, str1,str2,str3,str4,str5);
}
return s;
}
static string sbAppendTest()
{
string str = "";
for (int i=0; i < 5000; i++)
{
string str1, str2, str3, str4, str5;
str1 = test.Substring(rand.Next(5),20);
str2 = test.Substring(rand.Next(5),20);
str3 = test.Substring(rand.Next(5),20);
str4 = test.Substring(rand.Next(5),20);
str5 = test.Substring(rand.Next(5),20);
StringBuilder sb = new StringBuilder(str);
sb.Append(" ");
sb.Append(str1);
sb.Append(" ");
sb.Append(str2);
sb.Append(" ");
sb.Append(str3);
sb.Append(" ");
sb.Append(str4);
sb.Append(" ");
sb.Append(str5);
str = sb.ToString();
}
return str;
}
public static void Main(string [] args){
string str;
Stopwatch st = Stopwatch.StartNew();
str = normalAppendTest();
st.Stop();
Console.WriteLine("{0}\t{1}", st.ElapsedTicks.ToString(), str.Length.ToString());
st = Stopwatch.StartNew();
str = joinAppendTest();
st.Stop();
Console.WriteLine("{0}\t{1}", st.ElapsedTicks.ToString(), str.Length.ToString());
st = Stopwatch.StartNew();
str = formatAppendTest();
st.Stop();
Console.WriteLine("{0}\t{1}", st.ElapsedTicks.ToString(), str.Length.ToString());
st = Stopwatch.StartNew();
str = sbAppendTest();
st.Stop();
Console.WriteLine("{0}\t{1}", st.ElapsedTicks.ToString(), str.Length.ToString());
}
}

I couldn't find advantage of StringBuilder in .NET 2.0
ralfg
Julian12
My results with your code:
21402812 525000
20926371 525000
51609507 525000
25218750 525000
egank
As Dreadjr said nobody ever said StringBuilder was the fastest way to build strings. StringBuilder's benefit is in how it manages memory. Since strings are immutable in .NET once you create a string is exists until it is released from memory. If you change the string you get a new instance. So if you were to create an empty string and then concatenate to it 10 times you'd have 10 or 11 copies of the string in memory.
You should use StringBuilder when you are building arbitrary strings in piece-meal fashion. In C# calls to string + string +... are internally converter to a call to String.Concat. For up to 4 string values each one is pushed on the stack and then String.Concat is called. More than 4 string values results in a temporary string array being created, the elements being moved to the string array and then String.Concat called. Just to be complete you should avoid calling String.Format whenever possible (even if you have objects other than strings to convert) because the performance at runtime is high because of the string parsing. Honestly you'd figure the compiler could do a compile-time parse of the string and replace it with an optimized expression (like regular expressions) but it doesn't.
So in a nutshell you should use StringBuilder to build arbitrary strings to conserve memory usage. For fixed strings you can use the + operator for cleanliness but you could also use String.Concat.
Just my opinion,
Michael Taylor - 6/21/06
Igor Rosenberg
Though StringBuilder is faster in the rearranged code but I am not sure whether it represents the common scenario. The scenario I am trying to simulate is appending a small number of strings in many places of of the code. For example:
Console.WriteLine ("You got an error in your application " + applicationName + " at line " + lineNumber);
So the expression needs to evalate to a string before it ends. It can't keep adding strings and generate the final string afterwards. But the rearranged code, StringBuilder is not generating string inside the loop.
But I am perplexed why StringBuilder is slower in the original code. It was doing the same amount of operation as + operator. Does C# do optimization like deferred evaluation when appending strings
Info_Tech
With a rearrangement of the call to constructor and to ToString()
static string sbAppendTest() {
string str = "";
StringBuilder sb = new StringBuilder( str, 0, 0, 525001 );
for ( int i = 0; i < 5000; i++ ) {
string str1, str2, str3, str4, str5;
str1 = test.Substring( rand.Next( 5 ), 20 );
str2 = test.Substring( rand.Next( 5 ), 20 );
str3 = test.Substring( rand.Next( 5 ), 20 );
str4 = test.Substring( rand.Next( 5 ), 20 );
str5 = test.Substring( rand.Next( 5 ), 20 );
sb.Append( " " );
sb.Append( str1 );
sb.Append( " " );
sb.Append( str2 );
sb.Append( " " );
sb.Append( str3 );
sb.Append( " " );
sb.Append( str4 );
sb.Append( " " );
sb.Append( str5 );
}
return sb.ToString();
}
I get this kind of performance:
36785 525000
When I omit the string capacity argument to the constructor, I get this:
56840 525000
CharlieWasAnAngel
RakeshGupta