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

Tri*_*nko 19 c# performance coding-style readability equality

使用该object.ReferenceEquals方法使用时会有额外的开销((object)obj1 == (object)obj2)吗?

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

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

(object)obj != null
Run Code Online (Sandbox Code Playgroud)

相比于...

!object.ReferenceEquals(obj,null)
Run Code Online (Sandbox Code Playgroud)

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

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

Kon*_*lph 22

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

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

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

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

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


naw*_*fal 5

与这里的答案相反,我发现的(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());
}
Run Code Online (Sandbox Code Playgroud)

测试:

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<Person>.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<Person>.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<Person>.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<Person>.Default.Equals(p1, p2); //3700 ~ 3800ms

}, 100000000);
Run Code Online (Sandbox Code Playgroud)

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

结果在Debug配置上也一致...

如前所述,重点应该放在可读性/意义/揭示意图上。

  • 像几乎所有已发布的微基准测试一样,您没有考虑样本的方差。适当的微基准*需要*包括统计显着性分析,否则整个结果可能是fl幸。仅重复几次测试是不够的。此外,要在此处获得权威性答案,我们应该查看生成的代码(不是IL,而是JIT生成的机器代码),这将向我们展示我们需要知道的所有信息。问题在于x86 JIT实际上并没有执行很多内联,因此您的结果似乎合理。(在x64上,结果应该有所不同。) (2认同)