参考平等性能差异? ((object)obj1 ==(object)obj2)vs。object.ReferenceEquals(obj1,obj2)

使用object.ReferenceEquals方法是否有额外的开销((object)obj1 == (object)obj2)

在第一种情况下,将涉及静态方法调用,并且在两种情况下都涉及到对象的某种forms的转换。

即使编译器平衡了那些方法,那么不等式呢?

 (object)obj != null 

相比于…

 !object.ReferenceEquals(obj,null) 

我想在某些时候,会在!=运算符内发生逻辑否定,或者应用于ReferenceEquals方法的结果。 你怎么看?

还有可读性问题需要考虑。 在检查相等性时,ReferenceEquals似乎更清晰,但对于不平等,可能会错过!object.ReferenceEquals之前,而第一个变体中的!=很难被忽视。

使用object.ReferenceEquals方法是否有额外的开销

不。该方法直接包含最小IL描述来执行引用相等性检查(对于记录:它相当于VB的Is运算符)并且通常由JIT内联(特别是在定位x64时),因此没有开销。

关于可读性:我个人认为object.ReferenceEquals可能更具可读性(即使是否定forms),因为它明确表达了它的语义。 强制转换为object可能会让一些程序员感到困惑。

我刚刚发现了一篇讨论这个的文章 。 它更喜欢(object)x == y因为IL足迹较小。 它认为这可能有助于使用这种比较来内联方法X 但是(没有任何关于JIT的详细知识,但在逻辑上和直觉上)我认为这是错误的:如果JIT表现得像优化C ++编译器,它会在内联对ReferenceEquals的调用之后考虑该方法,因此(为了内联)方法X )内存占用将完全相同。

也就是说:选择一种方式而不是另一种方式对JIT没有任何影响,从而对性能没有任何影响。

与此处的答案相反,我发现(object) ==object.ReferenceEquals更快。 至于如何更快,非常微不足道!

试验台:

我知道你需要引用相等性检查,但是我还包括静态object.Equals(,)方法以及它不被覆盖的类。

平台:x86; 配置:发布版本

 class Person { } public static void Benchmark(Action method, int iterations = 10000) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < iterations; i++) method(); sw.Stop(); MsgBox.ShowDialog(sw.Elapsed.TotalMilliseconds.ToString()); } 

测试:

 Person p1 = new Person(); Person p2 = new Person(); bool b; Benchmark(() => { b = (object)p1 == (object)p2; //960 ~ 1000ms b = object.ReferenceEquals(p1, p2); //~ 1250ms b = object.Equals(p1, p2); //2100ms b = EqualityComparer.Default.Equals(p1, p2); //~4000ms }, 100000000); Person p1 = new Person(); Person p2 = null; bool b; Benchmark(() => { b = (object)p1 == (object)p2; //990 ~ 1000ms b = object.ReferenceEquals(p1, p2); // 1230 ~ 1260ms b = object.Equals(p1, p2); //1250 ~ 1300ms b = EqualityComparer.Default.Equals(p1, p2); //~3100ms }, 100000000); Person p1 = null; Person p2 = null; bool b; Benchmark(() => { b = (object)p1 == (object)p2; //960 ~ 1000ms b = object.ReferenceEquals(p1, p2); //1260 ~ 1270ms b = object.Equals(p1, p2); //1180 ~ 1220ms b = EqualityComparer.Default.Equals(p1, p2); //~3100ms }, 100000000); Person p1 = new Person(); Person p2 = p1; bool b; Benchmark(() => { b = (object)p1 == (object)p2; //960 ~ 1000ms b = object.ReferenceEquals(p1, p2); //1260 ~ 1280ms b = object.Equals(p1, p2); //1150 ~ 1200ms b = EqualityComparer.Default.Equals(p1, p2); //3700 ~ 3800ms }, 100000000); 

object.Equals(,)内部调用ReferenceEquals ,如果它们不相等,它将调用该类的覆盖虚拟Equals方法,因此您会看到速度差异的通知。

Debug配置中的结果也一致......

正如所指出的,重点应放在可读性/意义/揭示意图上。

Object.ReferenceEquals的开销仅在加载参数时,在大多数情况下都会被JIT实现。 之后,Object.ReferenceEquals和operator ==都归结为单个IL ceq运算符。 在最坏的情况下,差异将是微不足道的。

更重要的是,Object.ReferenceEquals比(object)o1 ==(object)o2更有意图揭示。 它在代码“我正在测试引用相等/身份”中清楚地说明,而不是将意图隐藏在一堆强制转换下。

经过很长时间的关键代码之后,在非常大的代码库中加上我的两分钱,有时候是疯狂的深度调用深度。

在现实世界中的“微观基准”之外,JIT还有更多的问题和关注点,而且他们有编译时间C ++ WPO的简洁性,也没有C#编译器更容易直接翻译,而且所有问题都没有。在C#编译器完成之后,确实拥有了所有上下文。

‘迂腐’forms:

 if ((object)a == (object)b) { } // ref equals if (!((object)a == (object)b)) { } // ref not equals 

如果你真的有诚实的上帝性能问题权衡和测量,或者需要从JIT中为一些非常普遍的类别施加压力,这可以帮助很多。 NullOrEmpty vs’(object)str == null ||也是如此 str.Length == 0’。

没有WPO的奢侈品,以及在许多情况下都不知道的所有限制因素,在JITing遭遇重击之后可能会加载或卸载哪些程序集,奇怪的非确定性事情会发生在优化的方面以及如何进行。

这是一个很大的话题,但这里有几点:

  1. 到目前为止JIT将追逐内联并优化注册优化深度 ,并且完全取决于当时正在发生的其他事情。 如果由于使用而最终在链上编译一次函数,并且在链的另一端进行一次不同的运行,则可以得到截然不同的结果。 对于受延迟或UI驱动约束的许多应用程序而言,最糟糕的事情是非消极化,而在较大的应用程序中,这可能会很快增加。

  2. !((object)a ==(object)b)和(object)a!=(object)b并不总是编译成相同的代码 ,对于!(a == b)和a!= b,即使没有任何明确的运算符或Equals覆盖。 一个’(对象)a!=(对象)b’更有可能触发.Net自己更迂腐的调用运行时非常昂贵。

  3. 即使您目前没有运营商或Equals覆盖,如果对JIT非常有帮助,也应尽早并经常使用’(object)’或’RefEquals’进行保护 。 (对象)更好。 如果你考虑JIT必须做什么来确定一个类型是否可以覆盖,并处理bizantine(sp)Equality规则等等,它就像一个迷你地狱,以及任何可以在以后公开的任何东西,你打算参考平等,你可以避免突然放缓或稍后的代码。 如果它已经是公开的而不是密封的,那么JIT就不能保证规则会或者有时间来追逐它们。

  4. 保护通常更普遍的“无效”检查可能更为重要 ,尽管不是OP问题的一部分,因为相同的规则和问题通常适用。 ‘(object)a == null’和’!((object)a == null)’是’迂腐’的等价物。

前面提到的关于==运算符更好的文章提供了不完整的信息,至少在.NET 4.0上(很好地它被写回2.0次)。

它声明ReferenceEquals没有被优化/内联,只有在使用’AnyCPU’配置构建项目时才会出现这种情况。 设置为’x86’或’x64’使(对象)obj == null和ReferenceEquals(object,null)最终成为相同的IL,其中两个方法都只是一个’ceq’IL指令。

所以答案是:两种方法生成的IL在Release / x86或x64版本上是相同的。

ReferenceEquals绝对更具可读性,至少符合我的口味。

 public static bool ReferenceEquals (Object objA, Object objB) { return objA == objB; } 

http://referencesource.microsoft.com/#mscorlib/system/object.cs,4d607d6d56a93c7e