C# 6.0 "string" 和 $"string" 之间的性能差异
Performance difference between C# 6.0 "string" and $"string"
我在 C# 6.0 中有 2 个代码:
示例 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.";
显然,无论审美差异如何,两种代码都会生成相同的结果。
问题:
两者之间有性能差异吗?换句话说,两种情况编译成一样吗?
编辑:
发布了一些重要的评论,我发现重播这些评论有助于澄清这个问题中的主题。
The second one is horrible. Don't use the string interpolation symbol ($) on strings that aren't interpolated. It will be confusing for anyone else looking at the code (or future, more experienced you)
感谢您的提示,非常感谢,尽管我不得不争辩说您所说的是美学论证。即使这段代码像你说的那样丑陋,即使永远不会被使用,我仍然认为讨论它可能产生的可能的性能差异是有用的。
@communityMember1: It doesn't affect performance of the code at runtime.
@communityMember2: That is not true. Interpolated string is syntactic sugar for calling
string.Format, which has to be called (and thus affecting
performance).
也许为了避免进一步的讨论,我们应该根据一些文档甚至是经验证明(比如汇编的结果)来加强答案和评论。
$
创建一个内插字符串,与使用 String.Format
相同。如果您将 +
与字符串文字一起使用,则编译器会优化代码以避免将它们串联在一起。使用内插字符串可能会阻止此优化。
编辑
性能和文字优化测试
好的,我刚刚对此进行了测试,似乎没有任何性能问题。显然,编译器会忽略 $
,如果没有 {}
,则不会使用 String.Format。我的测试结果如下所示,用于构造一个 50 长度的字符串的 600 万个循环。
String.Format
:23700 毫秒
$"a{null}"
与 +
:22650 毫秒
$"a"
与 +
:13 毫秒
"a"
与 +
:13 毫秒
"a"
与 +
连接:700 毫秒
此外,查看 IL 也没有区别。因此在性能方面,您发布的代码的执行和优化相同, $
被忽略并且仅在使用 {}
时成为一个因素。
语法
当然可以争论这是否是好的语法。我能想到支持和反对的论据。
优点
- 如果
$
已经存在,添加新参数会更容易
- 在多行文字的行之间移动参数更容易
- 它提供多行文字的行之间的一致性
缺点
- 它看起来不一样... 最不惊讶的原则。
- 为什么要添加未使用的语法?
- 插入的变量不明显
对我来说,倾斜点是我看到 $
并期望看到 {}
和一个论点。如果我没有看到我期望看到的东西,就会导致认知失调。另一方面,我可以想象它提供的流动性可能超出此范围的情况。说它取决于开发组和目标可能还很远。
这很容易进行一些简单的诊断。 Maccettura 在评论中表明,编译时没有区别,因为它只为每个字符串调用 String.Format 一次,而不管额外的插值,但此测试也会对其进行备份。
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
查看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: (Linqpad 5.22.02 生成的IL).
你可以选择任何你喜欢的。 $ 确实会转换为 string.format(请参阅 https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interpolated-strings),但编译器足够聪明,可以过滤掉未格式化的代码。
我在 C# 6.0 中有 2 个代码:
示例 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.";
显然,无论审美差异如何,两种代码都会生成相同的结果。
问题:
两者之间有性能差异吗?换句话说,两种情况编译成一样吗?
编辑:
发布了一些重要的评论,我发现重播这些评论有助于澄清这个问题中的主题。
The second one is horrible. Don't use the string interpolation symbol ($) on strings that aren't interpolated. It will be confusing for anyone else looking at the code (or future, more experienced you)
感谢您的提示,非常感谢,尽管我不得不争辩说您所说的是美学论证。即使这段代码像你说的那样丑陋,即使永远不会被使用,我仍然认为讨论它可能产生的可能的性能差异是有用的。
@communityMember1: It doesn't affect performance of the code at runtime.
@communityMember2: That is not true. Interpolated string is syntactic sugar for calling string.Format, which has to be called (and thus affecting performance).
也许为了避免进一步的讨论,我们应该根据一些文档甚至是经验证明(比如汇编的结果)来加强答案和评论。
$
创建一个内插字符串,与使用 String.Format
相同。如果您将 +
与字符串文字一起使用,则编译器会优化代码以避免将它们串联在一起。使用内插字符串可能会阻止此优化。
编辑
性能和文字优化测试
好的,我刚刚对此进行了测试,似乎没有任何性能问题。显然,编译器会忽略 $
,如果没有 {}
,则不会使用 String.Format。我的测试结果如下所示,用于构造一个 50 长度的字符串的 600 万个循环。
String.Format
:23700 毫秒$"a{null}"
与+
:22650 毫秒$"a"
与+
:13 毫秒"a"
与+
:13 毫秒"a"
与+
连接:700 毫秒
此外,查看 IL 也没有区别。因此在性能方面,您发布的代码的执行和优化相同, $
被忽略并且仅在使用 {}
时成为一个因素。
语法
当然可以争论这是否是好的语法。我能想到支持和反对的论据。
优点
- 如果
$
已经存在,添加新参数会更容易 - 在多行文字的行之间移动参数更容易
- 它提供多行文字的行之间的一致性
缺点
- 它看起来不一样... 最不惊讶的原则。
- 为什么要添加未使用的语法?
- 插入的变量不明显
对我来说,倾斜点是我看到 $
并期望看到 {}
和一个论点。如果我没有看到我期望看到的东西,就会导致认知失调。另一方面,我可以想象它提供的流动性可能超出此范围的情况。说它取决于开发组和目标可能还很远。
这很容易进行一些简单的诊断。 Maccettura 在评论中表明,编译时没有区别,因为它只为每个字符串调用 String.Format 一次,而不管额外的插值,但此测试也会对其进行备份。
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
查看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: (Linqpad 5.22.02 生成的IL).
你可以选择任何你喜欢的。 $ 确实会转换为 string.format(请参阅 https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interpolated-strings),但编译器足够聪明,可以过滤掉未格式化的代码。