为何Interlocked.Exchange<T>性能远低于Interlocked.CompareExchange<T>?
Interlocked.Exchange slower than Interlocked.CompareExchange for reference types in .NET Core? Great catch on this weird performance discrepancy! Let's break down exactly what's going on here, and tackle your questions one by one.
Key Observations to Ground This
First, let's recap the critical details from your benchmark and investigation that point us to the root cause:
- For value types like
intorlong, both methods perform nearly identically, and disassemble directly to the CPU'sxchgandcmpxchginstructions—exactly what you'd expect for low-level interlocked operations. - For reference types (like your
stringtest),Interlocked.Exchangeruns roughly three times slower thanInterlocked.CompareExchange, and both show up as framework method calls in disassembly instead of direct CPU instructions.
The Real Cause: .NET Core Implementation Differences
This gap has nothing to do with CPU instruction performance—your value type test confirms that xchg and cmpxchg are roughly equivalent. Instead, it's all about how .NET Core implements these two methods for reference types:
The Interlocked.Exchange overload for reference types includes extra runtime checks and write barrier logic that the CompareExchange reference type overload skips. These additional operations add measurable overhead that shows up clearly in your benchmarks.
Your findings align with the issue you raised with the coreclr team, where they'll dig into whether this is an intentional design tradeoff or a missed optimization opportunity.
Should You Replace Exchange with CompareExchange?
Short answer: Only if the semantic behavior exactly matches what your code needs.
It's tempting to swap to the faster method, but don't forget—these two methods have fundamentally different purposes:
Interlocked.Exchange: Unconditionally swaps the reference with the new value, no questions asked, and returns the original value.Interlocked.CompareExchange: Only swaps the reference if it matches the expected value you pass in; if not, it leaves things as-is and returns the current value.
If your code requires an unconditional swap, using CompareExchange (even with null as the expected value) will only work if you can guarantee the current reference is actually null. Otherwise, it'll fail to swap silently, introducing hard-to-debug concurrency bugs. Correctness always comes before performance.
Wrapping Up
This is a perfect example of how framework implementation details can create unexpected performance gaps, especially with low-level concurrency primitives. For reference types, the current .NET Core implementation creates this slowdown in Exchange, but it's not a reflection of what the CPU is capable of.
内容的提问来源于stack exchange,提问作者Shay Rojansky

