c# - Interlocked.Exchange<T> 比 Interlocked.CompareExchange<T> 慢?

标签 c# .net .net-core compare-and-swap interlocked

我在优化程序时遇到了一些奇怪的性能结果,这些结果显示在以下 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.ExchangeInterlocked.CompareExchange 慢两倍以上 - 这令人困惑,因为它应该做更少的工作。除非我弄错了,否则两者都应该是 CPU 操作。

有没有人能很好地解释为什么会发生这种情况?这是 CPU 操作的实际性能差异还是 .NET Core 包装它们的方式中的某些问题?

如果是这种情况,似乎最好避免 Interlocked.Exchange() 并尽可能使用 Interlocked.CompareExchange()

编辑:另一件奇怪的事情:当我使用 int 或 long 而不是 string 运行相同的基准测试时,我得到或多或少相同的运行时间。此外,我使用 BenchmarkDotNet 的反汇编程序诊断器来查看实际生成的程序集,并发现了一些有趣的东西:使用 int/long 版本我可以清楚地看到 xchg 和 cmpxchg 指令,但是使用字符串我看到对 Interlocked.Exchange/Interlocked 的调用。 CompareExchange 方法...!

EDIT2:在 coreclr 中打开的问题:https://github.com/dotnet/coreclr/issues/16051

最佳答案

根据我的评论,这似乎是 Exchange 的通用重载的问题.

如果您完全避免泛型重载(将 _s_y 的类型更改为 object ),性能差异就会消失。

问题仍然存在,但为什么解决通用重载只会减慢速度 Exchange .通读Interlocked源代码,似乎在 CompareExchange<T> 中实现了黑客攻击让它更快。 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 团队的某个人来真正证实我的怀疑。

关于c# - Interlocked.Exchange<T> 比 Interlocked.CompareExchange<T> 慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48470081/

相关文章:

c# - 在 .net core 上创建 EF 迁移时出现 System.IO.FileNotFoundException

c# - Azure函数版本2.0-应用程序blobTrigger不工作

c# - 在 C# 控制台应用程序中呈现 SSRS;在哪里可以找到 SSRS ReportExecutionService?

c# - 学习有效地使用接口(interface)

.net - 为什么 Visual Studio 中 'Add Reference' 对话框中的 .NET 选项卡没有列出 GAC 的内容?

.net - 在没有 WebBrowser 控件的 Windows.Forms 中托管 Silverlight

c# - Xamarin 从 2.5 更新到 3.5 后,Xamarin.Forms.Platform.Android 不存在

nginx - .Net 核心 X-Forwarded-Proto header 无法正确传递给 Nginx

c# - 为什么 DynamicMethod 在 x64 上这么慢?

oracle - ORA-12570 : Network Session: Unexpected packet read error