首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Interlocked.Exchange<T>比Interlocked.CompareExchange<T>慢吗?

Interlocked.Exchange<T>比Interlocked.CompareExchange<T>慢吗?
EN

Stack Overflow用户
提问于 2018-01-27 05:51:29
回答 2查看 377关注 0票数 7

在优化程序时,我遇到了一些奇怪的性能结果,如以下BenchmarkDotNet基准测试所示:

代码语言:javascript
复制
string _s, _y = "yo";

[Benchmark]
public void Exchange() => Interlocked.Exchange(ref _s, null);

[Benchmark]
public void CompareExchange() => Interlocked.CompareExchange(ref _s, _y, null);

结果如下:

代码语言:javascript
复制
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操作的实际性能差异,还是.NET核心包装它们的方式存在问题?

如果是这种情况,那么最好的办法就是尽可能避免使用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

EN

回答 2

Stack Overflow用户

发布于 2018-01-27 07:08:16

根据我的评论,这似乎是Exchange的泛型重载的一个问题。

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

问题仍然存在,为什么解决泛型重载只会降低Exchange的速度。通读Interlocked源代码,似乎在CompareExchange中实现了一个使其更快的黑客攻击。CompareExchange<T>上的源代码注释如下:

代码语言:javascript
复制
 * 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中没有类似的注释,而且它还使用了“极其缓慢”的__makeref,所以这可能是您看到这种意外行为的原因。

所有这一切当然是我的解释,你实际上需要.NET团队的某个人来真正证实我的怀疑。

票数 7
EN

Stack Overflow用户

发布于 2020-01-19 15:29:41

此问题现已在较新版本的.Net核心中得到修复:

https://github.com/dotnet/coreclr/issues/16051

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48470081

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档