2

今日、私は次のことに遭遇しました:

次の「等しい」メソッドを持つ2つのクラスNewClassとNewClass1について考えてみます。

NewClass:

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

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

NewClass1:

@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のequalsがNewClassのequalsよりも指数関数的に遅いように見えることです(10.000.000は3000msに対して14msを呼び出します)。最初は、これは「instanceof」チェックに関連していると思いましたが、「return equals((NewClass1)obj);」を置き換えると 「falseを返す;」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

時間を費やしているのはテストのインスタンスだと思います。そのメソッドの最終的な戻り値を常にfalseを返すように変更すると、評価に関係なく結果が同じになる(falseを返す)ため、コンパイラーはおそらく条件を削除します。これは、最終的な戻り値を変更しても効果がない理由も説明します。これは、あなたが言うように、コードパスで実際に到達することは決してないからです。

より一般的に言えば、コードの変更は、コンパイラーがコードを最適化する方法を変更することにより、実行されたコードパス上にない場合でもパフォーマンスに影響を与える可能性があります。

于 2010-12-14T13:22:06.760 に答える
2

最初の例equals(NewClass)では、通常は呼び出されません。equals(Object)HotSpot(または同様のもの)によってインライン化することができ、テストの本体を実質的にゼロに減らすことができます。

エンベロープ計算の裏側は有益な場合があります。「10.000.000は8msを呼び出します」は、1秒間に1,250,000,000回の反復です。4 GHzプロセッサを想定すると、反復ごとに約3サイクルになります。やりがいのあることをするのは少し速いです。実際、コードは10,000,000ではなく1,000,000,000と言っています。

実際、実際のコードでは、すべてのループ本体を削除できます。したがって、何を測定するかは実際には重要ではありません。有用なものを確実に示すことはできません。マイクロベンチマークの実行には他にも多くの問題があり、他の多くの場所で読むことができます。

于 2010-12-14T13:04:37.103 に答える
1

最初の例では、常にfalseを返します。これは非常に高速です。2番目の例では、はるかに長い比較アルゴリズムがあります

于 2010-12-14T13:06:20.550 に答える
0

さて、最初の例はほとんど何もしません。反復回数を100000に減らすことができます。これも同じ結果、5または6ミリ秒になります。これは、JVMがコードのその部分を積極的に最適化することを意味します。

于 2010-12-14T14:49:28.127 に答える