C# 6.0 "string" 和 "string" 美元之间的性能差异



我在C#6.0中有两个代码:

样本1:

string bar;
// ... some code setting bar.
var foo =
"Some text, " +
$"some other {bar}, " +
"end text.";

样本2:

string bar;
// ... some code setting bar.
var foo =
$"Some text, " +
$"some other {bar}, " +
$"end text.";

显然,无论审美差异如何,这两种代码都会产生相同的结果。

问题:
两者之间是否存在性能差异?换言之,这两种情况是否被编译为相同?

编辑:
发布了一些重要评论,我觉得这些评论有助于重播,以澄清本问题中的主题。

第二个太可怕了。不要在未插值的字符串上使用字符串插值符号($)。对于任何其他人来说(或者未来,更有经验的你)都会感到困惑

谢谢你的提示,非常感谢,尽管我不得不说你说的是美学论证。即使这个代码和你说的一样丑陋,即使永远不应该使用,我仍然认为讨论它可能产生的性能差异是有用的。

@communityMember1:它不会影响代码在运行时的性能
@communityMember2:这不是真的。插入字符串是调用的语法糖一串格式,必须调用(并因此影响性能)。

也许为了避免进一步的讨论,我们应该根据一些文件甚至经验证明(比如汇编的结果)来强化答案和评论。

$创建一个插值字符串,该字符串与使用String.Format相同。如果将+与字符串文字一起使用,则编译器会优化代码以避免将所有代码串联在一起。使用插值字符串可能会阻止此优化。

编辑


性能和文字优化测试

好吧,我刚刚测试了一下,似乎没有任何性能问题。显然,编译器会忽略$,如果不存在{},则不使用String.Format。我的测试结果如下所示,600万个循环构建了一个50长度的字符串。

  • String.Format:23700毫秒
  • $"a{null}"+:22650ms
  • $"a"+:13ms
  • "a"+:13ms
  • "a"+级联:700ms

此外,查看IL也没有区别。因此,从性能角度来看,您发布的代码执行和优化的方式相同,$会被忽略,并且只是使用{}时的一个因素。

语法

当然,这是否是一个好的语法可能会引起争论。我能想出支持和反对的论据。

Pros

  • 如果$已经存在,添加新参数会更容易
  • 在多行文字的行之间移动参数更容易
  • 它提供了多行文字的行之间的一致性

缺点

  • 看起来不一样。。。最不惊讶的原则
  • 为什么要添加未使用的语法
  • 它不会使插入的变量变得明显

对我来说,倾斜点是我看到$,并期望看到{}和一个自变量。如果我看不到我期望看到的东西,就会导致认知失调。另一方面,我可以想象它提供的流动性可能会超出这个范围。要说它取决于开发团队和目标,可能还远远不够。

查看IL:

第一个代码:

var bar = "a";
var foo = "b" + $"{bar}";

第一个IL:

IL_0000:  nop         
IL_0001:  ldstr       "a"
IL_0006:  stloc.0     // bar
IL_0007:  ldstr       "b"
IL_000C:  ldstr       "{0}"
IL_0011:  ldloc.0     // bar
IL_0012:  call        System.String.Format
IL_0017:  call        System.String.Concat
IL_001C:  stloc.1     // foo
IL_001D:  ret         

第二个代码:

var bar = "a";
var foo = $"b" + $"{bar}";

第二个IL:

IL_0000:  nop         
IL_0001:  ldstr       "a"
IL_0006:  stloc.0     // bar
IL_0007:  ldstr       "b"
IL_000C:  ldstr       "{0}"
IL_0011:  ldloc.0     // bar
IL_0012:  call        System.String.Format
IL_0017:  call        System.String.Concat
IL_001C:  stloc.1     // foo
IL_001D:  ret         

结论:

这两个代码之间没有经过编译的差异。

PS:(IL由Linqpad 5.22.02生成)。

这很容易进行一些简单的诊断。Maccettura在评论中显示,当它被编译时没有区别,因为它只调用了String。为每个字符串格式化一次,无论是否有额外的插值,但这次测试也会备份它。

int rounds = 50;
int timesToCreateString = 50000;
Stopwatch sw = new Stopwatch();
double first = 0;
double second = 0;
for (int i = 0; i < rounds; i++)
{
sw.Start();
for (int bar = 0; bar < timesToCreateString; bar++)
{
var foo = "Some text, " +
$"some other {bar}, " +
"end text.";
}
sw.Stop();
first += sw.ElapsedTicks;
sw.Reset();
sw.Start();
for (int bar = 0; bar < timesToCreateString; bar++)
{                
var foo = $"Some text, " +
$"some other {bar}, " +
$"end text.";
}
sw.Stop();
second += sw.ElapsedTicks;
sw.Reset();
}
Console.WriteLine("Average first test: " + first / rounds);
Console.WriteLine("Average second test: " + second / rounds);
Console.ReadKey();
// program ran 3 times and results.
//Average first test: 54822.04
//Average second test: 55083.86
//Average first test: 54317.66
//Average second test: 54807.8
//Average first test: 49873.12
//Average second test: 48264.36

您可以选择任何您喜欢的。确实,$转换为字符串格式(请参阅https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interpolated-strings),但编译器足够聪明,可以过滤掉未格式化的代码。

最新更新