Что Java делает с моими "равными" реализациями здесь? - PullRequest
2 голосов
/ 14 декабря 2010

Сегодня я наткнулся на следующее:

Рассмотрим два класса 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 кажутся экспоненциально медленнее, чем в NewClass (для 10.000.000 вызовов 14мс против 3000мс).Сначала я думал, что это связано с проверкой instanceof, но если я заменю «return equals ((NewClass1) obj);»с "вернем ложь";в NewClass1 внезапно он работает более или менее одинаково быстро.Я не очень понимаю, что здесь происходит, потому что, по моему мнению, оператор return в equals (Object) никогда не должен вызываться.Что я тут не так делаю?

Ниже приведен мой «код для тестирования», если я допустил ошибку:

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 ]

3 голосов
/ 14 декабря 2010

Я полагаю, что это экземпляр теста, который потребляет время. Когда вы изменяете окончательное возвращение в этом методе так, чтобы оно всегда возвращало false, компилятор, вероятно, удаляет условное выражение, поскольку результат будет одинаковым (возвращаем false) независимо от его оценки. Это также объясняет, почему изменение окончательного возврата вообще имеет какой-либо эффект, поскольку, как вы говорите, оно никогда не должно быть достигнуто в пути кода.

В целом, изменение кода может повлиять на производительность, даже если оно не находится на пути к исполняемому коду, путем изменения способа оптимизации кода компилятором.

2 голосов
/ 14 декабря 2010

В первом примере equals(NewClass) обычно никогда не вызывается.equals(Object) может быть встроен HotSpot (или подобным), и тело вашего теста может быть фактически сведено к нулю.

Задняя часть вычислений разработчика может быть информативной«10.000.000 вызовов 8 мс» - это 1 250 000 000 итераций в секунду.Предполагая процессор 4 ГГц, это примерно три цикла на итерацию.Немного быстро, чтобы сделать что-нибудь стоящее.Фактически в коде написано 1 000 000 000, а не 10 000 000.

Фактически в реальном коде можно исключить все тело цикла.Таким образом, на самом деле не имеет значения, что вы измеряете - это не будет надежной индикацией чего-либо полезного.Есть много других проблем с выполнением микробенчмарков, которые вы можете прочитать во многих других местах.

1 голос
/ 14 декабря 2010

В первом примере вы всегда возвращаете false.Это очень быстроВо втором примере у вас гораздо более длинный алгоритм сравнения

0 голосов
/ 14 декабря 2010

Ну, первый пример почти ничего не делает. Вы можете уменьшить номер итерации до 100000, снова вы получите тот же результат, 5 или 6 мс. Это означает, что JVM агрессивно оптимизирует эту часть вашего кода.

...