当我优化一个程序时,发现了一些奇怪的性能结果,这些结果在下面的BenchmarkDotNet基准测试中显示:
string _s, _y = "yo";
[Benchmark]
public void Exchange() => Interlocked.Exchange(ref _s, null);
[Benchmark]
public void CompareExchange() => Interlocked.CompareExchange(ref _s, _y, null);
以下是结果:
BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
.NET Core SDK=2.1.4
[Host] : .NET Core 2.0.5 (Framework 4.6.26020.03), 64bit RyuJIT
DefaultJob : .NET Core 2.0.5 (Framework 4.6.26020.03), 64bit RyuJIT
Method | Mean | Error | StdDev |
---------------- |----------:|----------:|----------:|
Exchange | 20.525 ns | 0.4357 ns | 0.4662 ns |
CompareExchange | 7.017 ns | 0.1070 ns | 0.1001 ns |
看起来,
Interlocked.Exchange
的速度比 Interlocked.CompareExchange
慢两倍以上——这很令人困惑,因为前者应该做的工作更少。除非我弄错了,这两个都应该是 CPU 操作。
有没有人能够解释一下为什么会出现这种情况?这是 CPU 操作中实际存在的性能差异,还是 .NET Core 包装它们的方式存在问题?
如果是这种情况,最好避免使用 Interlocked.Exchange()
,尽可能使用 Interlocked.CompareExchange()
?
编辑:另一个奇怪的事情是:当我使用 int 或 long 而不是字符串运行相同的基准测试时,我得到的运行时间大致相同。此外,我使用 BenchmarkDotNet 的反汇编诊断器查看生成的实际汇编代码,并发现了一些有趣的东西:对于 int/long 版本,我可以清楚地看到 xchg 和 cmpxchg 指令,但对于字符串,我看到了调用 Interlocked.Exchange/Interlocked.CompareExchange 方法的指令...!
编辑2:在 coreclr 中打开了一个问题:https://github.com/dotnet/coreclr/issues/16051