哪个字符串串联操作更快+";或字符串.凹面
本文关键字:字符串 quot 凹面 操作 | 更新日期: 2023-09-27 17:59:18
我一直在阅读不同的答案,哪种字符串串联操作更好。我在某个地方读到"+"在内部调用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代码中注意到,"+"只初始化一个字符串,而字符串。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代码中得出结论,"+"比"string.Concat"更快,因为它使用较小的变量来执行相同的操作吗?
这种比较是错误的(假设您对字符串常量的串联不感兴趣)。
在您的第一个代码段中,连接已经由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);
}
现在看看他们各自的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没有)。
为了说明性能,我们使用了评测器,因为它们可以为我们提供实用、详细的指标。
String类中没有Operator +
,因此整个+
转换为String.Concat
是由C#编译器完成的,因此它们是相同的。
停止对代码进行微优化或过早优化。试着编写一个执行正确的代码,然后如果以后遇到性能问题,则对应用程序进行评测,看看问题出在哪里。字符串是不可变的。如果您有一段代码由于字符串串联而出现性能问题,则应该考虑使用StringBuilder。
我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源。然而我们不应该通过在关键的3%中增加我们的机会