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

m_d*_*p29 2 .net c# string

我一直在阅读不同的答案,其中字符串连接操作更好.我读到某处"+"内部调用string.Concat方法,string.Concat两者之间更快.当我查看IL代码时,它似乎没有提出上述陈述.

对于

string s1 = "1" + "2";
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

对于,

string s1 = string.concat("1", "2");
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

The*_*kis 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
}
Run Code Online (Sandbox Code Playgroud)

在您的第二个片段中,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
}
Run Code Online (Sandbox Code Playgroud)

因此,尝试使用常量来辨别两个片段的性能是没有意义的,因为您将获得非代表性的结果.

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

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

static string Plus(string a, string b, string c)
{
    return a + b + c;
}
Run Code Online (Sandbox Code Playgroud)

其他电话string.Concat:

static string Concat(string a, string b, string c)
{
    return string.Concat(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)

现在使用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
}
Run Code Online (Sandbox Code Playgroud)

和:

.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
}
Run Code Online (Sandbox Code Playgroud)

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

总之,在这种特殊情况下,我们可以安全地假设我们不会观察到表达同一事物的两种方式之间的任何性能差异.

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

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


Ham*_*jam 6

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

停止对代码进行微优化过早优化.尝试编写一个正确执行的代码,然后如果您以后遇到性能问题,请分析您的应用程序并查看问题所在.字符串是不可变的.如果您有一段代码因字符串连接而出现性能问题,则应考虑使用StringBuilder.

我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源.然而,我们不应该在那个关键的3%中放弃我们的机会