2

今天,我偶然发现了以下内容:

考虑两个类 NewClass 和 NewClass1,它们具有以下“等于”方法:

新类:

@Override
public boolean equals(Object obj) {
    return false;
}

public boolean equals(NewClass obj) {
    return value == obj.getValue();
}

新类1:

@Override
public boolean equals(Object obj) {
    if(!(obj instanceof NewClass1)) {
        return false;
    }
    return equals((NewClass1) obj);
}

public boolean equals(NewClass1 obj) {
    return value == obj.getValue();
}

我觉得奇怪的是 NewClass1 中的等号似乎比 NewClass 中的等号慢(对于 10.000.000 调用 14ms 对 3000ms)。起初,我以为这与“instanceof”检查有关,但如果我替换“return equals((NewClass1) obj);” 用“返回假;” 在 NewClass1 中,它突然运行或多或少同样快。我真的不明白这里发生了什么,因为在我看来,equals(Object) 中的 return 语句实际上不应该被调用。我在这里做错了什么?

以下是我的“基准代码”,以防我在那里犯了一些错误:

public static void main(String[] args) {
    // TODO code application logic here

    NewClass i1 = new NewClass(1);
    NewClass i2 = new NewClass(1);
    NewClass i3 = new NewClass(5);

    NewClass1 j1 = new NewClass1(1);
    NewClass1 j2 = new NewClass1(1);
    NewClass1 j3 = new NewClass1(5);

    Object o1 = new Object();
    Object o2 = new Object();


    assert(i1.equals(i1));
    assert(i1.equals(i2));
    assert(i1.equals(i3) == false);
    assert(i1.equals(o1) == false);

    assert(j1.equals(j1));
    assert(j1.equals(j2));
    assert(j1.equals(j3) == false);
    assert(j1.equals(o1) == false);


    long start = System.currentTimeMillis();

    for(int i=0; i<1000000000; i++) {
        i1.equals(i1);
        i1.equals(i2);
        i1.equals(o1);
        i1.equals(o2);
    }

    long end = System.currentTimeMillis();

    System.out.println("Execution time was "+(end-start)+" ms.");



    start = System.currentTimeMillis();

    for(int i=0; i<1000000000; i++) {
        j1.equals(j1);
        j1.equals(j2);
        j1.equals(o1);
        j1.equals(o2);
    }

    end = System.currentTimeMillis();

    System.out.println("Execution time was "+(end-start)+" ms.");
}
4

4 回答 4

3

我猜想是 instanceof 测试在消耗时间。当您将该方法中的最终返回更改为始终返回 false 时,编译器可能会消除条件,因为无论其评估如何,结果都是相同的(返回 false)。这也可以解释为什么更改最终返回有任何影响,因为正如您所说,它实际上不应该在代码路径中达到。

更一般地说,即使代码更改不在执行的代码路径上,代码更改也会通过更改编译器优化代码的方式来影响性能。

于 2010-12-14T13:22:06.760 回答
2

在第一个例子equals(NewClass)中通常不会被调用。equals(Object)可以通过 HotSpot (或类似的)内联,并且您的测试主体可以有效地减少到无。

信封计算的背面可以提供信息。“10.000.000 次调用 8 毫秒”是每秒 1,250,000,000 次迭代。假设一个 4 GHz 处理器,每次迭代大约需要三个周期。做任何有价值的事情有点快。事实上,代码说的是 1,000,000,000 而不是 10,000,000。

事实上,在实际代码中,所有的循环体都可以被消除。所以,你测量什么并不重要——它不会是任何有用的可靠指示。做微基准测试还有许多其他问题,您可以在许多其他地方阅读。

于 2010-12-14T13:04:37.103 回答
1

在第一个示例中,您总是返回 false。这是非常快的。在第二个示例中,您有一个更长的比较算法

于 2010-12-14T13:06:20.550 回答
0

好吧,第一个例子几乎什么也没做。你可以将迭代次数减少到 100000,再次得到相同的结果,5 或 6 毫秒。这意味着 JVM 会积极优化您的代码的那部分。

于 2010-12-14T14:49:28.127 回答