本文通过ANTS Memory Profiler工具探索c#中+、string.Concat、string.Format、StringBuilder.Append四种方式进行字符串拼接时的性能。

本文涉及程序为.NET Core 2.0控制台应用程序。

一、常量字符串拼接

private static void TestPerformance(Action action, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i = ; i < times; i++)
{
action();
}
sw.Stop();
Console.WriteLine(action.Method.Name + ", FullTime: " + sw.ElapsedMilliseconds);
}

常量字符串测试方法

1.+方法

1.1连续拼接

private static void AddTest()
{
string s = string.Empty;
s = "" + "" + "" + "" + "" + "" + "" + "";
}

+连续拼接常量字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  AddTest() cil managed
{
// Code size 7 (0x7)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: pop
IL_0006: ret
} // end of method Program::AddTest

1.2分段拼接

private static void AddWithParamTest2()
{
string s = string.Empty;
s += "";
s += "";
s += "";
s += "";
s += "";
s += "";
s += "";
s += "";
}

分段拼接常量字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  AddWithParamTest2() cil managed
{
// Code size 87 (0x57)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: ldstr ""
IL_000a: call string [System.Runtime]System.String::Concat(string,
string)
IL_000f: ldstr ""
IL_0014: call string [System.Runtime]System.String::Concat(string,
string)
IL_0019: ldstr ""
IL_001e: call string [System.Runtime]System.String::Concat(string,
string)
IL_0023: ldstr ""
IL_0028: call string [System.Runtime]System.String::Concat(string,
string)
IL_002d: ldstr ""
IL_0032: call string [System.Runtime]System.String::Concat(string,
string)
IL_0037: ldstr ""
IL_003c: call string [System.Runtime]System.String::Concat(string,
string)
IL_0041: ldstr ""
IL_0046: call string [System.Runtime]System.String::Concat(string,
string)
IL_004b: ldstr ""
IL_0050: call string [System.Runtime]System.String::Concat(string,
string)
IL_0055: pop
IL_0056: ret
} // end of method Program::AddWithParamTest2

通过IL代码可以看出,分段的+=代码调用的是Concat方法,并且比连续的+多开辟了许多内存空间。

2.Concat方法

2.1分次Concat

private static void ConcatTest()
{
string s = string.Empty;
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
s = string.Concat(s, "");
}

分次Concat常量字符串

IL代码:

.method private hidebysig static void  AddWithParamTest2() cil managed
{
// Code size 87 (0x57)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: ldstr ""
IL_000a: call string [System.Runtime]System.String::Concat(string,
string)
IL_000f: ldstr ""
IL_0014: call string [System.Runtime]System.String::Concat(string,
string)
IL_0019: ldstr ""
IL_001e: call string [System.Runtime]System.String::Concat(string,
string)
IL_0023: ldstr ""
IL_0028: call string [System.Runtime]System.String::Concat(string,
string)
IL_002d: ldstr ""
IL_0032: call string [System.Runtime]System.String::Concat(string,
string)
IL_0037: ldstr ""
IL_003c: call string [System.Runtime]System.String::Concat(string,
string)
IL_0041: ldstr ""
IL_0046: call string [System.Runtime]System.String::Concat(string,
string)
IL_004b: ldstr ""
IL_0050: call string [System.Runtime]System.String::Concat(string,
string)
IL_0055: pop
IL_0056: ret
} // end of method Program::AddWithParamTest2

可见IL代码与+分段拼接常量字符串相同,性能相似。

2.2Concat一次拼接常量字符串

private static void ConcatTest2()
{
string s = string.Empty;
string.Concat(s, "", "", "", "", "", "", "", "");
}

Concat一次拼接常量字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  ConcatTest2() cil managed
{
// Code size 88 (0x58)
.maxstack
.locals init (string V_0)
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: stloc.
IL_0006: ldc.i4.s
IL_0008: newarr [System.Runtime]System.String
IL_000d: dup
IL_000e: ldc.i4.
IL_000f: ldloc.
IL_0010: stelem.ref
IL_0011: dup
IL_0012: ldc.i4.
IL_0013: ldstr ""
IL_0018: stelem.ref
IL_0019: dup
IL_001a: ldc.i4.
IL_001b: ldstr ""
IL_0020: stelem.ref
IL_0021: dup
IL_0022: ldc.i4.
IL_0023: ldstr ""
IL_0028: stelem.ref
IL_0029: dup
IL_002a: ldc.i4.
IL_002b: ldstr ""
IL_0030: stelem.ref
IL_0031: dup
IL_0032: ldc.i4.
IL_0033: ldstr ""
IL_0038: stelem.ref
IL_0039: dup
IL_003a: ldc.i4.
IL_003b: ldstr ""
IL_0040: stelem.ref
IL_0041: dup
IL_0042: ldc.i4.
IL_0043: ldstr ""
IL_0048: stelem.ref
IL_0049: dup
IL_004a: ldc.i4.
IL_004b: ldstr ""
IL_0050: stelem.ref
IL_0051: call string [System.Runtime]System.String::Concat(string[])
IL_0056: stloc.0
IL_0057: ret
} // end of method Program::ConcatTest2

通过IL代码可以看出,string.Concat(s, s1, s2, s3)并不调用String.Concat方法,而是直接在堆栈上进行操作。因为需要局部变量来标记,比分次调用Concat方法所占的内存要多一些。

3.Format方法

3.1一次Format

private static void FormatTest()
{
string s = string.Empty;
s = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", "", "", "", "", "", "", "", "");
}

Format常量字符串拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  FormatTest() cil managed
{
// Code size 88 (0x58)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: pop
IL_0006: ldstr "{0}{1}{2}{3}{4}{5}{6}{7}"
IL_000b: ldc.i4.
IL_000c: newarr [System.Runtime]System.Object
IL_0011: dup
IL_0012: ldc.i4.
IL_0013: ldstr ""
IL_0018: stelem.ref
IL_0019: dup
IL_001a: ldc.i4.
IL_001b: ldstr ""
IL_0020: stelem.ref
IL_0021: dup
IL_0022: ldc.i4.
IL_0023: ldstr ""
IL_0028: stelem.ref
IL_0029: dup
IL_002a: ldc.i4.
IL_002b: ldstr ""
IL_0030: stelem.ref
IL_0031: dup
IL_0032: ldc.i4.
IL_0033: ldstr ""
IL_0038: stelem.ref
IL_0039: dup
IL_003a: ldc.i4.
IL_003b: ldstr ""
IL_0040: stelem.ref
IL_0041: dup
IL_0042: ldc.i4.
IL_0043: ldstr ""
IL_0048: stelem.ref
IL_0049: dup
IL_004a: ldc.i4.
IL_004b: ldstr ""
IL_0050: stelem.ref
IL_0051: call string [System.Runtime]System.String::Format(string,
object[])
IL_0056: pop
IL_0057: ret
} // end of method Program::FormatTest

StringFormat方法虽然是基于StringBuilder,但由于需要遍历字符串来识别占位符,所以比较慢。

3.2Format分次拼接

private static void FormatTest2()
{
string s = string.Empty;
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
s = string.Format("{0}{1}", s, "");
}

Format分次拼接常量字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  FormatTest2() cil managed
{
// Code size 143 (0x8f)
.maxstack
.locals init (string V_0)
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: stloc.
IL_0006: ldstr "{0}{1}"
IL_000b: ldloc.
IL_000c: ldstr ""
IL_0011: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_0016: stloc.
IL_0017: ldstr "{0}{1}"
IL_001c: ldloc.
IL_001d: ldstr ""
IL_0022: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_0027: stloc.
IL_0028: ldstr "{0}{1}"
IL_002d: ldloc.
IL_002e: ldstr ""
IL_0033: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_0038: stloc.
IL_0039: ldstr "{0}{1}"
IL_003e: ldloc.
IL_003f: ldstr ""
IL_0044: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_0049: stloc.
IL_004a: ldstr "{0}{1}"
IL_004f: ldloc.
IL_0050: ldstr ""
IL_0055: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_005a: stloc.
IL_005b: ldstr "{0}{1}"
IL_0060: ldloc.
IL_0061: ldstr ""
IL_0066: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_006b: stloc.
IL_006c: ldstr "{0}{1}"
IL_0071: ldloc.
IL_0072: ldstr ""
IL_0077: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_007c: stloc.
IL_007d: ldstr "{0}{1}"
IL_0082: ldloc.
IL_0083: ldstr ""
IL_0088: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_008d: stloc.
IL_008e: ret
} // end of method Program::FormatTest2

分次使用Format方法拼接字符串,即分次调用Format方法,多次循环遍历字符串,耗时更长。

4.StringBuilder方法

private static void BuilderTest()
{
StringBuilder sb = new StringBuilder();
sb.Append("");
sb.Append("");
sb.Append("");
sb.Append("");
sb.Append("");
sb.Append("");
sb.Append("");
sb.Append("");
}

Builder拼接常量字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  BuilderTest() cil managed
{
// Code size 101 (0x65)
.maxstack
IL_0000: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor()
IL_0005: dup
IL_0006: ldstr ""
IL_000b: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0010: pop
IL_0011: dup
IL_0012: ldstr ""
IL_0017: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_001c: pop
IL_001d: dup
IL_001e: ldstr ""
IL_0023: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0028: pop
IL_0029: dup
IL_002a: ldstr ""
IL_002f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0034: pop
IL_0035: dup
IL_0036: ldstr ""
IL_003b: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0040: pop
IL_0041: dup
IL_0042: ldstr ""
IL_0047: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_004c: pop
IL_004d: dup
IL_004e: ldstr ""
IL_0053: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0058: pop
IL_0059: ldstr ""
IL_005e: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0063: pop
IL_0064: ret
} // end of method Program::BuilderTest

在短字符串大量拼接的测试中,可以看出一次使用+进行拼接所耗内存最少,所用时间最短。其余方式所耗内存相差不大,但StringBuilder所耗时间明显较短。

由于Format方法内部存在对字符串的遍历,可以推测,随着字符串的长度变长,Format方法所耗时间将会增加。

二、字符串变量拼接(多次循环拼接)

private static void TestPerformanceWithParam<T>(Action<T> action, T t, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = ; i < times; i++)
{
action(t);
}
sw.Stop();
Console.WriteLine(action.Method.Name + ", FullTime: " + sw.ElapsedMilliseconds);
}

变量字符串测试方法

1.+方法

1.1连续拼接

private static void AddTest(string t)
{
string s = string.Empty;
s = t + t + t + t + t + t + t + t;
}

Plus字符串变量连续拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  AddTest(string t) cil managed
{
// Code size 51 (0x33)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: pop
IL_0006: ldc.i4.
IL_0007: newarr [System.Runtime]System.String
IL_000c: dup
IL_000d: ldc.i4.
IL_000e: ldarg.
IL_000f: stelem.ref
IL_0010: dup
IL_0011: ldc.i4.
IL_0012: ldarg.
IL_0013: stelem.ref
IL_0014: dup
IL_0015: ldc.i4.
IL_0016: ldarg.
IL_0017: stelem.ref
IL_0018: dup
IL_0019: ldc.i4.
IL_001a: ldarg.
IL_001b: stelem.ref
IL_001c: dup
IL_001d: ldc.i4.
IL_001e: ldarg.
IL_001f: stelem.ref
IL_0020: dup
IL_0021: ldc.i4.
IL_0022: ldarg.
IL_0023: stelem.ref
IL_0024: dup
IL_0025: ldc.i4.
IL_0026: ldarg.
IL_0027: stelem.ref
IL_0028: dup
IL_0029: ldc.i4.
IL_002a: ldarg.
IL_002b: stelem.ref
IL_002c: call string [System.Runtime]System.String::Concat(string[])
IL_0031: pop
IL_0032: ret
} // end of method Program::AddTest

Plus字符串变量拼接IL

从IL代码可见,在使用+连续拼接字符串变量时,内部调用了String.Concat(string[])方法。

1.2分段拼接

private static void AddWithParamTest2(string t)
{
string s = string.Empty;
s += t;
s += t;
s += t;
s += t;
s += t;
s += t;
s += t;
s += t;
}

Plus字符串变量拼接分段

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  AddWithParamTest2(string t) cil managed
{
// Code size 55 (0x37)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: ldarg.
IL_0006: call string [System.Runtime]System.String::Concat(string,
string)
IL_000b: ldarg.
IL_000c: call string [System.Runtime]System.String::Concat(string,
string)
IL_0011: ldarg.
IL_0012: call string [System.Runtime]System.String::Concat(string,
string)
IL_0017: ldarg.
IL_0018: call string [System.Runtime]System.String::Concat(string,
string)
IL_001d: ldarg.
IL_001e: call string [System.Runtime]System.String::Concat(string,
string)
IL_0023: ldarg.
IL_0024: call string [System.Runtime]System.String::Concat(string,
string)
IL_0029: ldarg.
IL_002a: call string [System.Runtime]System.String::Concat(string,
string)
IL_002f: ldarg.
IL_0030: call string [System.Runtime]System.String::Concat(string,
string)
IL_0035: pop
IL_0036: ret
} // end of method Program::AddWithParamTest2

2.Concat方法

2.1分次concat

IL代码:

.method private hidebysig static void  ConcatTest(string t) cil managed
{
// Code size 55 (0x37)
.maxstack
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: ldarg.
IL_0006: call string [System.Runtime]System.String::Concat(string,
string)
IL_000b: ldarg.
IL_000c: call string [System.Runtime]System.String::Concat(string,
string)
IL_0011: ldarg.
IL_0012: call string [System.Runtime]System.String::Concat(string,
string)
IL_0017: ldarg.
IL_0018: call string [System.Runtime]System.String::Concat(string,
string)
IL_001d: ldarg.
IL_001e: call string [System.Runtime]System.String::Concat(string,
string)
IL_0023: ldarg.
IL_0024: call string [System.Runtime]System.String::Concat(string,
string)
IL_0029: ldarg.
IL_002a: call string [System.Runtime]System.String::Concat(string,
string)
IL_002f: ldarg.
IL_0030: call string [System.Runtime]System.String::Concat(string,
string)
IL_0035: pop
IL_0036: ret
} // end of method Program::ConcatTest

从IL代码来看,同使用+分段拼接相似。

2.2一次拼接

IL代码:

.method private hidebysig static void  ConcatTest2(string t) cil managed
{
// Code size 56 (0x38)
.maxstack
.locals init (string V_0)
IL_0000: ldsfld string [System.Runtime]System.String::Empty
IL_0005: stloc.
IL_0006: ldc.i4.s
IL_0008: newarr [System.Runtime]System.String
IL_000d: dup
IL_000e: ldc.i4.
IL_000f: ldloc.
IL_0010: stelem.ref
IL_0011: dup
IL_0012: ldc.i4.
IL_0013: ldarg.
IL_0014: stelem.ref
IL_0015: dup
IL_0016: ldc.i4.
IL_0017: ldarg.
IL_0018: stelem.ref
IL_0019: dup
IL_001a: ldc.i4.
IL_001b: ldarg.
IL_001c: stelem.ref
IL_001d: dup
IL_001e: ldc.i4.
IL_001f: ldarg.
IL_0020: stelem.ref
IL_0021: dup
IL_0022: ldc.i4.
IL_0023: ldarg.
IL_0024: stelem.ref
IL_0025: dup
IL_0026: ldc.i4.
IL_0027: ldarg.
IL_0028: stelem.ref
IL_0029: dup
IL_002a: ldc.i4.
IL_002b: ldarg.
IL_002c: stelem.ref
IL_002d: dup
IL_002e: ldc.i4.
IL_002f: ldarg.
IL_0030: stelem.ref
IL_0031: call string [System.Runtime]System.String::Concat(string[])
IL_0036: stloc.
IL_0037: ret
} // end of method Program::ConcatTest2

从IL代码可以看出,同使用+一次拼接相似。

3.Format

private static void FormatTest(string t)
{
string s = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", t, t, t, t, t, t, t, t);
}

Format字符串变量拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  FormatTest(string t) cil managed
{
// Code size 50 (0x32)
.maxstack
IL_0000: ldstr "{0}{1}{2}{3}{4}{5}{6}{7}"
IL_0005: ldc.i4.
IL_0006: newarr [System.Runtime]System.Object
IL_000b: dup
IL_000c: ldc.i4.
IL_000d: ldarg.
IL_000e: stelem.ref
IL_000f: dup
IL_0010: ldc.i4.
IL_0011: ldarg.
IL_0012: stelem.ref
IL_0013: dup
IL_0014: ldc.i4.
IL_0015: ldarg.
IL_0016: stelem.ref
IL_0017: dup
IL_0018: ldc.i4.
IL_0019: ldarg.
IL_001a: stelem.ref
IL_001b: dup
IL_001c: ldc.i4.
IL_001d: ldarg.
IL_001e: stelem.ref
IL_001f: dup
IL_0020: ldc.i4.
IL_0021: ldarg.
IL_0022: stelem.ref
IL_0023: dup
IL_0024: ldc.i4.
IL_0025: ldarg.
IL_0026: stelem.ref
IL_0027: dup
IL_0028: ldc.i4.
IL_0029: ldarg.
IL_002a: stelem.ref
IL_002b: call string [System.Runtime]System.String::Format(string,
object[])
IL_0030: pop
IL_0031: ret
} // end of method Program::FormatTest

时间的损耗主要来自于String.Format方法。

4.StringBuilder方法

private static void BuilderTest(string t)
{
StringBuilder sb = new StringBuilder();
sb.Append(t);
sb.Append(t);
sb.Append(t);
sb.Append(t);
sb.Append(t);
sb.Append(t);
sb.Append(t);
sb.Append(t);
}

Builder字符串变量拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  BuilderTest(string t) cil managed
{
// Code size 69 (0x45)
.maxstack
IL_0000: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor()
IL_0005: dup
IL_0006: ldarg.
IL_0007: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_000c: pop
IL_000d: dup
IL_000e: ldarg.
IL_000f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0014: pop
IL_0015: dup
IL_0016: ldarg.
IL_0017: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_001c: pop
IL_001d: dup
IL_001e: ldarg.
IL_001f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0024: pop
IL_0025: dup
IL_0026: ldarg.
IL_0027: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_002c: pop
IL_002d: dup
IL_002e: ldarg.
IL_002f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0034: pop
IL_0035: dup
IL_0036: ldarg.
IL_0037: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_003c: pop
IL_003d: ldarg.
IL_003e: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0043: pop
IL_0044: ret
} // end of method Program::BuilderTest

在短字符串(上面所用字符串为“short”)大量循环拼接(上述结果为每个方法循环执行一千万次)时,四种方法的差距并不明显。

当字符串变长后,可见运行结果差距拉大:

此时StringBuilder仍没有明显的优势。

三、字符串变量拼接(循环拼接至一个字符串)

1.+方法

private static void TestAddVeryLong(string s, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
string str = string.Empty;
for(int i = ; i < times; i++)
{
str += s;
}
sw.Stop();
Console.WriteLine("add very long: " + sw.ElapsedMilliseconds);
}

Plus长字符串拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  TestAddVeryLong(string s,
int32 times) cil managed
{
// Code size 71 (0x47)
.maxstack
.locals init (class [System.Runtime.Extensions]System.Diagnostics.Stopwatch V_0,
string V_1,
int32 V_2)
IL_0000: newobj instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.
IL_0006: ldloc.
IL_0007: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Start()
IL_000c: ldsfld string [System.Runtime]System.String::Empty
IL_0011: stloc.
IL_0012: ldc.i4.
IL_0013: stloc.
IL_0014: br.s IL_0022
IL_0016: ldloc.
IL_0017: ldarg.
IL_0018: call string [System.Runtime]System.String::Concat(string,
string)
IL_001d: stloc.
IL_001e: ldloc.
IL_001f: ldc.i4.
IL_0020: add
IL_0021: stloc.
IL_0022: ldloc.
IL_0023: ldarg.
IL_0024: blt.s IL_0016
IL_0026: ldloc.
IL_0027: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Stop()
IL_002c: ldstr "add very long: "
IL_0031: ldloc.
IL_0032: callvirt instance int64 [System.Runtime.Extensions]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_0037: box [System.Runtime]System.Int64
IL_003c: call string [System.Runtime]System.String::Concat(object,
object)
IL_0041: call void [System.Console]System.Console::WriteLine(string)
IL_0046: ret
} // end of method Program::TestAddVeryLong

2.Concat方法

private static void TestConcatVeryLong(string s, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
string str = string.Empty;
for (int i = ; i < times; i++)
{
str = string.Concat(str, s);
}
sw.Stop();
Console.WriteLine("concat very long: " + sw.ElapsedMilliseconds);
}

Concat长字符串拼接

IL代码:

.method private hidebysig static void  TestConcatVeryLong(string s,
int32 times) cil managed
{
// Code size 71 (0x47)
.maxstack
.locals init (class [System.Runtime.Extensions]System.Diagnostics.Stopwatch V_0,
string V_1,
int32 V_2)
IL_0000: newobj instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.
IL_0006: ldloc.
IL_0007: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Start()
IL_000c: ldsfld string [System.Runtime]System.String::Empty
IL_0011: stloc.
IL_0012: ldc.i4.
IL_0013: stloc.
IL_0014: br.s IL_0022
IL_0016: ldloc.
IL_0017: ldarg.
IL_0018: call string [System.Runtime]System.String::Concat(string,
string)
IL_001d: stloc.
IL_001e: ldloc.
IL_001f: ldc.i4.
IL_0020: add
IL_0021: stloc.
IL_0022: ldloc.
IL_0023: ldarg.
IL_0024: blt.s IL_0016
IL_0026: ldloc.
IL_0027: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Stop()
IL_002c: ldstr "concat very long: "
IL_0031: ldloc.
IL_0032: callvirt instance int64 [System.Runtime.Extensions]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_0037: box [System.Runtime]System.Int64
IL_003c: call string [System.Runtime]System.String::Concat(object,
object)
IL_0041: call void [System.Console]System.Console::WriteLine(string)
IL_0046: ret
} // end of method Program::TestConcatVeryLong

由IL代码可见,和使用+性能相似。

3.Format方法

private static void TestFormatVeryLong(string s, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
string str = string.Empty;
for (int i = ; i < times; i++)
{
str = string.Format("{0}{1}", str, s);
}
sw.Stop();
Console.WriteLine("format very long: " + sw.ElapsedMilliseconds);
}

Format长字符串拼接

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  TestFormatVeryLong(string s,
int32 times) cil managed
{
// Code size 76 (0x4c)
.maxstack
.locals init (class [System.Runtime.Extensions]System.Diagnostics.Stopwatch V_0,
string V_1,
int32 V_2)
IL_0000: newobj instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.
IL_0006: ldloc.
IL_0007: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Start()
IL_000c: ldsfld string [System.Runtime]System.String::Empty
IL_0011: stloc.
IL_0012: ldc.i4.
IL_0013: stloc.
IL_0014: br.s IL_0027
IL_0016: ldstr "{0}{1}"
IL_001b: ldloc.
IL_001c: ldarg.
IL_001d: call string [System.Runtime]System.String::Format(string,
object,
object)
IL_0022: stloc.
IL_0023: ldloc.
IL_0024: ldc.i4.
IL_0025: add
IL_0026: stloc.
IL_0027: ldloc.
IL_0028: ldarg.
IL_0029: blt.s IL_0016
IL_002b: ldloc.
IL_002c: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Stop()
IL_0031: ldstr "format very long: "
IL_0036: ldloc.
IL_0037: callvirt instance int64 [System.Runtime.Extensions]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_003c: box [System.Runtime]System.Int64
IL_0041: call string [System.Runtime]System.String::Concat(object,
object)
IL_0046: call void [System.Console]System.Console::WriteLine(string)
IL_004b: ret
} // end of method Program::TestFormatVeryLong

4.StringBuilder方法

private static void TestBuilderVeryLong(string s, int times)
{
Stopwatch sw = new Stopwatch();
sw.Start();
StringBuilder sb = new StringBuilder();
for (int i = ; i < times; i++)
{
sb.Append(s);
}
sw.Stop();
Console.WriteLine("builder very long: " + sw.ElapsedMilliseconds);
}

Builder拼接长字符串

运行时间:

内存情况:

IL代码:

.method private hidebysig static void  TestBuilderVeryLong(string s,
int32 times) cil managed
{
// Code size 71 (0x47)
.maxstack
.locals init (class [System.Runtime.Extensions]System.Diagnostics.Stopwatch V_0,
class [System.Runtime]System.Text.StringBuilder V_1,
int32 V_2)
IL_0000: newobj instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.
IL_0006: ldloc.
IL_0007: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Start()
IL_000c: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor()
IL_0011: stloc.
IL_0012: ldc.i4.
IL_0013: stloc.
IL_0014: br.s IL_0022
IL_0016: ldloc.
IL_0017: ldarg.
IL_0018: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_001d: pop
IL_001e: ldloc.
IL_001f: ldc.i4.
IL_0020: add
IL_0021: stloc.
IL_0022: ldloc.
IL_0023: ldarg.
IL_0024: blt.s IL_0016
IL_0026: ldloc.
IL_0027: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Stop()
IL_002c: ldstr "builder very long: "
IL_0031: ldloc.
IL_0032: callvirt instance int64 [System.Runtime.Extensions]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_0037: box [System.Runtime]System.Int64
IL_003c: call string [System.Runtime]System.String::Concat(object,
object)
IL_0041: call void [System.Console]System.Console::WriteLine(string)
IL_0046: ret
} // end of method Program::TestBuilderVeryLong

这个差距在拼接字符串更多的时候会更加明显。

拼接一千万次时,+方法运行一个多小时仍无法得出结果:

而StringBuilder只需要102ms。

+方法的问题在于由于string的特殊性,每次操作都将新开辟内存空间,随着字符串长度的增加、操作次数的增加等,+方法所耗费的内存会越来越大。

而StringBuilder则可以在原有的内存空间中进行修改和扩张,在进行频繁、长串的操作时极大地提高了效率。

显然,如果是不频繁、短字符串的拼接,使用哪种方式拼接字符串没有明显的性能差别(Format可能稍差一些),但如果是频繁的长串操作,StringBuilder具有绝对的优势。

C# 字符串拼接性能探索的更多相关文章

  1. C# 字符串拼接性能探索 c#中+、string.Concat、string.Format、StringBuilder.Append四种方式进行字符串拼接时的性能

    本文通过ANTS Memory Profiler工具探索c#中+.string.Concat.string.Format.StringBuilder.Append四种方式进行字符串拼接时的性能. 本文 ...

  2. golang字符串拼接性能对比

    对比 +(运算符).strings.Join.sprintf.bytes.Buffer对字符串拼接的性能 package main import ( "bytes" "f ...

  3. C# 利用StringBuilder提升字符串拼接性能

    一个项目中有数据图表呈现,数据量稍大时显得很慢. 用Stopwatch分段监控了一下,发现耗时最多的函数是SaveToExcel 此函数中遍列所有数据行,通过Replace替换标签生成Excel行,然 ...

  4. 操作 html 的时候是使用 dom 方法还是字符串拼接?

    比如一个列表里面有很多个 li,要给他们加上数据.但多少个 li 是不确定的,由后台数据确定.这时候,就要动态生成 html 内容了. 那么,这个过程, 是使用 += 方法把标签.数据进行一个个的字符 ...

  5. javascript中字符串拼接详解

    字符串拼接是所有程序设计语言都需要的操作.当拼接结果较长时,如何保证效率就成为一个很重要的问题.本文介绍的是Javascript中的字符串拼接,希望对你有帮助,一起来看.   最近在研究<jav ...

  6. 【转】Java 5种字符串拼接方式性能比较。

    最近写一个东东,可能会考虑到字符串拼接,想了几种方法,但对性能未知,于是用Junit写了个单元测试. 代码如下: import java.util.ArrayList; import java.uti ...

  7. Java 5种字符串拼接方式性能比较。

    最近写一个东东,可能会考虑到字符串拼接,想了几种方法,但对性能未知,于是用Junit写了个单元测试. 代码如下: import java.util.ArrayList; import java.uti ...

  8. Java中测试StringBuilder、StringBuffer、String在字符串拼接上的性能

    应一个大量字符串拼接的任务 测试一下StringBuilder.StringBuffer.String在操作字符串拼接时候的性能 性能上理论是StringBuilder  >  StringBu ...

  9. java字符串格式化性能对比String.format/StringBuilder/+拼接

    String.format由于每次都有生成一个Formatter对象,因此速度会比较慢,在大数据量需要格式化处理的时候,避免使用String.format进行格式化,相反使用StringUtils.l ...

随机推荐

  1. okhttp 调用相机 上传服务器

    MainActivity package com.bwie.lianxi1; import android.content.Intent; import android.database.Cursor ...

  2. Docker集群管理工具 - Kubernetes 部署记录 (运维小结)

    一.  Kubernetes 介绍 Kubernetes是一个全新的基于容器技术的分布式架构领先方案, 它是Google在2014年6月开源的一个容器集群管理系统,使用Go语言开发,Kubernete ...

  3. JVM自动内存管理机制——Java内存区域(上)

    一.JVM运行时数据区域概述 Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄 ...

  4. Android开发 - 更"聪明"的申请权限方式

    在Android6.0以后,很多权限需要动态申请,只有在用户点同意后,我们才能使用对应API,因此,正确申请权限就显得很重要. 常用方式 通常我们使用这种方式来判断权限状态: private stat ...

  5. C语言小程序——推箱子(窄字符和宽字符)

    C语言小程序——推箱子(窄字符Version) 推箱子.c #include <stdio.h> #include <conio.h> #include <stdlib. ...

  6. Android 上传文件到 FTP 服务器

    实现背景 近期接触到一个需求,就是将文件从Android系统上传到FTP服务器,虽然之前接触过FTP服务器,了解基本的使用流程,但是将此流程从使用习惯转化为代码实现还是有一定难度的.但是基本的流程还是 ...

  7. LabVIEW(十一):条件结构的巧用

    一.LabVIEW中条件结构使用起来并不是那么简便,主要体现在两点: 1.由隧道的产生引起的一些问题.(当箭头停留在隧道处时不显示为“自动索引隧道”,所以此隧道非彼隧道) 2.由多层结构判断引起的不易 ...

  8. PHP错误处理函数register_shutdown_function

    当程序在线上运行时,如果遇到BUG,想不在前端输出错误信息,同时能及时邮件通知开发者,register_shutdown_function函数就可以派上用场了. 注册一个会在脚本执行完成或者 exit ...

  9. 关于No mapping found for HTTP request with URI...

    当你看到这篇博客,说明你已经像我一样快疯了!但是还好..找到了!!! 网上的那些东西真心看了更闹心,因为还没解决... 原因一.访问路径确实错了,导致Dispatcher找不到给你传达的地方,修改下领 ...

  10. jsp-9大内置对象简介

    产生背景 JSP引擎在调用JSP对应的jspServlet时,会传递或创建9个与web开发相关的对象供jspServlet使用.JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引 ...