哪个字符串连接操作更快 - " +"或string.Concat

时间:2015-06-24 12:22:57

标签: c# .net string

我一直在阅读关于字符串连接操作更好的不同答案。我读到的地方" +"内部调用string.Concat方法,string.Concat两者之间的速度更快。当我查看IL代码时,它似乎没有建议上述陈述。

有关

string s1 = "1" + "2";

IL代码

.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

另外,我从IL代码中注意到" +"只初始化一个字符串而string.Concat初始化两个字符串以连接。我也试过多个字符串。在使用内存方面," +"似乎只使用一个字符串变量,而另一个选项在内部使用更多变量。

对,

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

IL代码

.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

我们可以从上面的IL代码中得出结论" +"比#34; string.Concat"更快因为它使用较小的变量来执行相同的操作?

2 个答案:

答案 0 :(得分:6)

这种比较是错误的(假设您对字符串常量的连接不感兴趣)。

在您的第一个片段中,连接已由C#编译器执行:

.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
}

在您的第二个代码段中,对string.Concat的调用仍然存在:

.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
}

因此,尝试使用常量来辨别两个片段的效果毫无意义,因为您将获得非代表性的结果。

在一般情况下,C#编译器将在字符串上编译+个运算符链,作为对string.Concat的单个调用。您可以通过使用变量而不是常量执行几乎相同的测试来验证这一点。

作为演示,请考虑这两种C#方法。一个使用+来连接字符串:

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

其他人致电string.Concat

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

现在使用Debug配置查看各自的IL:

.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
}

.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
}

它们是相同的(除了它们的名字)。如果我们使用Release配置构建,我们会得到更短的IL - 但两种方法仍然相同。

总之,在这种特殊情况下,我们可以放心地假设我们没有观察到表达同一事物的两种方式之间的任何性能差异。

在一般情况下(IL不相同或几乎相同),我们无法根据CLR的心理模型对性能做出任何假设。即使我们有一个完全准确的CLR心智模型,我们必须考虑到字节码最终得到编译的机器代码,这与IL不同(例如,x86代码有寄存器但IL没有按'。T)

为了说明性能,我们使用分析器,因为它们可以为我们提供实用,详细的指标。

答案 1 :(得分:6)

String class中没有Operator +,因此转换为+的整个String.Concat由C#编译器完成,因此它们是相同的。

停止对代码执行micro-optimizingpremature-optimization。尝试编写一个正确执行的代码,然后如果您以后遇到性能问题,请分析您的应用程序并查看问题所在。字符串是immutable。如果您有一段因字符串连接而出现性能问题的代码,则应考虑使用StringBuilder

  

我们应该忘记效率低,大约97%的时间说:   过早优化是万恶之源。但我们不应该通过   我们在关键的3%

中获得机会