C# 字符串插值分配

C# 字符串插值分配,c#,.net-core,benchmarking,microbenchmark,C#,.net Core,Benchmarking,Microbenchmark,我正在运行一些基准测试来确定内插字符串的堆分配,特别是在它们内部使用值类型。我举一个例子: var str = $"Hello world {Guid.NewGuid()}"; 这会导致装箱,因为插入的字符串是string.Format和string.Format接受对象的语法糖。您可以看到以下内容的IL: IL_0000: ldstr "Hello world {0}" IL_0005: call valuetype [System.Private.CoreLib]System.Guid [

我正在运行一些基准测试来确定内插字符串的堆分配,特别是在它们内部使用值类型。我举一个例子:

var str = $"Hello world {Guid.NewGuid()}";
这会导致装箱,因为插入的字符串是
string.Format
string.Format
接受
对象的语法糖。您可以看到以下内容的
IL

IL_0000: ldstr "Hello world {0}"
IL_0005: call valuetype [System.Private.CoreLib]System.Guid [System.Private.CoreLib]System.Guid::NewGuid()
IL_000a: box [System.Private.CoreLib]System.Guid
IL_000f: call string [System.Private.CoreLib]System.String::Format(string, object)
IL_0014: ret
因此,避免这种装箱的解决方案是在
Guid
上调用
ToString()
,这是一种
struct
,此方法已经过验证

IL
对于此

IL_0000: call valuetype [System.Private.CoreLib]System.Guid [System.Private.CoreLib]System.Guid::NewGuid()
IL_0005: stloc.1
IL_0006: ldloca.s 1
IL_0008: constrained. [System.Private.CoreLib]System.Guid
IL_000e: callvirt instance string [System.Private.CoreLib]System.Object::ToString()
IL_0013: stloc.0
IL_0014: ldstr "Hello world "
IL_0019: ldloc.0
IL_001a: call string [System.Private.CoreLib]System.String::Concat(string, string)
IL_001f: ret
我的第一个基准测试是确认调用
Guid.ToString()
实际上分配的虚拟调用少于一个装箱的虚拟调用。i、 e

var str = Guid.ToString();
分配少于

object o = Guid.NewGuid();
var str = o.ToString();
事实确实如此。所以在这之后,我希望
$“Hello world{Guid.NewGuid().ToString()}”
分配的资源更少。我的假设大错特错。此方法比装箱方法多分配72个字节

为什么会这样?下面是我使用的基准和结果

[Benchmark]
public static string NoBox()
{
    return Guid.NewGuid().ToString();
}

[Benchmark]
public static string Box()
{
    object o = Guid.NewGuid();
    return o.ToString();
}

[Benchmark]
public static string WithToString()
{
    return $"Hello world {Guid.NewGuid().ToString()}";
}

[Benchmark]
public static string WithoutToString()
{
    return $"Hello world {Guid.NewGuid()}";
}


|          Method |     Mean |    Error |   StdDev |  Gen 0 |  Gen 1 | Gen 2 | Allocated |
|---------------- |---------:|---------:|---------:|-------:|-------:|------:|----------:|
|           NoBox | 215.9 ns | 5.362 ns | 15.47 ns | 0.0246 |      - |     - |     104 B |
|             Box | 224.8 ns | 4.563 ns | 11.78 ns | 0.0322 | 0.0002 |     - |     136 B |
|    WithToString | 238.7 ns | 4.856 ns | 11.91 ns | 0.0553 |      - |     - |     232 B |
| WithoutToString | 337.2 ns | 6.771 ns | 17.24 ns | 0.0381 |      - |     - |     160 B |

别忘了,Guid.ToString和string.Format也有实现。在我看来,如果您真的想比较苹果和苹果,您需要在“装箱”场景中使用
$“Hello world{((object)Guid.NewGuid()).ToString()}”
。因为
string.Format()
string.Concat()
之间的差异可以很容易地解释观察到的差异。还有,你有没有费心去看看分配了什么?仅仅知道分配的字节数似乎没有多大用处。@Mad,我不确定我是否理解你的意思。我希望调用
Guid.ToString()
分配的资源少于对其进行装箱,然后调用对象上的
ToString()
,但当它在插值字符串中完成时,这并不是说您是对的,我们可以说很有趣。为什么的答案可能嵌套在非琐碎的地方,主要是实现细节,更不用说可能会随着未来版本的变化而变化。现在的任何答案在将来都可能无效。所以你走在正确的轨道上,如果你需要对此进行微观优化,那么现在就做你正在做的事情,找到最适合你的方式。除此之外,我们真的没有什么可以补充的了。。用最好的方式说不要忘记,Guid.ToString和string.Format也有实现。在我看来,如果你真的想比较苹果和苹果,你需要在你的“装箱”场景中使用
$“Hello world{((object)Guid.NewGuid()).ToString()}”
。因为
string.Format()
string.Concat()
之间的差异可以很容易地解释观察到的差异。还有,你有没有费心去看看分配了什么?仅仅知道分配的字节数似乎没有多大用处。@Mad,我不确定我是否理解你的意思。我希望调用
Guid.ToString()
分配的资源少于对其进行装箱,然后调用对象上的
ToString()
,但当它在插值字符串中完成时,这并不是说您是对的,我们可以说很有趣。为什么的答案可能嵌套在非琐碎的地方,主要是实现细节,更不用说可能会随着未来版本的变化而变化。现在的任何答案在将来都可能无效。所以你走在正确的轨道上,如果你需要对此进行微观优化,那么现在就做你正在做的事情,找到最适合你的方式。除此之外,我们真的没有什么可以补充的了。。用最好的方式说
[Benchmark]
public static string NoBox()
{
    return Guid.NewGuid().ToString();
}

[Benchmark]
public static string Box()
{
    object o = Guid.NewGuid();
    return o.ToString();
}

[Benchmark]
public static string WithToString()
{
    return $"Hello world {Guid.NewGuid().ToString()}";
}

[Benchmark]
public static string WithoutToString()
{
    return $"Hello world {Guid.NewGuid()}";
}


|          Method |     Mean |    Error |   StdDev |  Gen 0 |  Gen 1 | Gen 2 | Allocated |
|---------------- |---------:|---------:|---------:|-------:|-------:|------:|----------:|
|           NoBox | 215.9 ns | 5.362 ns | 15.47 ns | 0.0246 |      - |     - |     104 B |
|             Box | 224.8 ns | 4.563 ns | 11.78 ns | 0.0322 | 0.0002 |     - |     136 B |
|    WithToString | 238.7 ns | 4.856 ns | 11.91 ns | 0.0553 |      - |     - |     232 B |
| WithoutToString | 337.2 ns | 6.771 ns | 17.24 ns | 0.0381 |      - |     - |     160 B |