[英]Interlocked.Exchange<T> slower than Interlocked.CompareExchange<T>?
我在优化程序时遇到了一些奇怪的性能结果,如下面的 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 而不是 string 运行相同的基准测试时,我或多或少地获得了相同的运行时间。 此外,我使用 BenchmarkDotNet 的反汇编器诊断程序查看正在生成的实际程序集,并发现了一些有趣的东西:使用 int/long 版本我可以清楚地看到 xchg 和 cmpxchg 指令,但使用字符串我看到调用 Interlocked.Exchange/Interlocked。比较交换方法...!
EDIT2:在 coreclr 中打开问题: https : //github.com/dotnet/coreclr/issues/16051
跟进我的评论,这似乎是Exchange
通用过载的问题。
如果您完全避免通用重载(将_s
和_y
的类型更改为object
),性能差异就会消失。
问题仍然存在,为什么解析到通用重载只会减慢Exchange
速度。 CompareExchange<T>
Interlocked
源代码,似乎在CompareExchange<T>
实施了一个 hack 以使其更快。 CompareExchange<T>
源代码注释如下:
* CompareExchange<T>
*
* Notice how CompareExchange<T>() uses the __makeref keyword
* to create two TypedReferences before calling _CompareExchange().
* This is horribly slow. Ideally we would like CompareExchange<T>()
* to simply call CompareExchange(ref Object, Object, Object);
* however, this would require casting a "ref T" into a "ref Object",
* which is not legal in C#.
*
* Thus we opted to cheat, and hacked to JIT so that when it reads
* the method body for CompareExchange<T>() it gets back the
* following IL:
*
* ldarg.0
* ldarg.1
* ldarg.2
* call System.Threading.Interlocked::CompareExchange(ref Object, Object, Object)
* ret
*
* See getILIntrinsicImplementationForInterlocked() in VM\JitInterface.cpp
* for details.
Exchange<T>
没有任何类似的评论,并且它还使用了“非常慢”的__makeref
因此这可能是您看到这种意外行为的原因。
所有这些当然是我的解释,您实际上需要 .NET 团队的某个人来真正证实我的怀疑。
现在已在较新版本的 .Net Core 上修复此问题:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.