Подтвердить что ты не робот

Является ли Objects.requireNonNull менее эффективным, чем старый?

С JDK 7 я с радостью использовал метод, который он представил, чтобы отклонить значения null, которые передаются методу, который не может их принять:

private void someMethod(SomeType pointer, SomeType anotherPointer) {
    Objects.requireNonNull(pointer, "pointer cannot be null!");
    Objects.requireNonNull(anotherPointer, "anotherPointer cannot be null!");
    // Rest of method
}

Я думаю, что этот метод делает очень аккуратный код, который легко читается, и я пытаюсь побудить коллег использовать его. Но один (особенно знающий) коллега устойчив и говорит, что старый способ более эффективен:

private void someMethod(SomeType pointer, SomeType anotherPointer) {
    if (pointer == null) {
        throw new NullPointerException("pointer cannot be null!");
    }
    if (anotherPointer == null) {
        throw new NullPointerException("anotherPointer cannot be null!");
    }
    // Rest of method
}

Он говорит, что вызов requireNonNull предполагает размещение другого метода в стеке вызовов JVM и приведет к худшей производительности, чем простая проверка == null.

Итак, мой вопрос: есть ли доказательства для оценки производительности, вызванного использованием методов Objects.requireNonNull?

4b9b3361

Ответ 1

Посмотрите на реализацию requireNonNull в Oracle JDK:

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

Итак, очень просто. JVM (в любом случае, Oracle) включает оптимизирующий двухэтапный компилятор "точно в момент времени" для преобразования байт-кода в машинный код. Он будет включать в себя тривиальные методы, подобные этому, если он может получить лучшую производительность таким образом.

Так что нет, вряд ли будет медленнее, ни в каком значимом смысле, нигде не имеет значения.

Итак, мой вопрос: есть ли доказательства неустойки производительности, связанные с использованием методов Objects.requireNonNull?

Единственное доказательство, которое имело бы значение, - это измерение производительности вашей кодовой базы или кода, который должен быть очень репрезентативным. Вы можете протестировать это с помощью любого достойного инструмента тестирования производительности, но если ваш коллега не сможет указать на реальный пример проблемы производительности в вашей кодовой базе, связанной с этим методом (а не синтетическим эталоном), я бы склонен предположить, что вы и у него/нее есть большая рыба, чтобы жарить.


Как немного в стороне, я заметил, что ваш метод sample - это метод private. Таким образом, только код, который ваша команда называет, вызывает его прямо. В таких ситуациях вы можете посмотреть, есть ли у вас вариант использования утверждений, а не проверки времени выполнения. Утверждения имеют то преимущество, что они вообще не выполняются в "выпущенном" коде и, таким образом, быстрее, чем любая альтернатива в вашем вопросе. Очевидно, что есть места, где вам нужны проверки времени выполнения, но они обычно находятся в пунктах проверки, общедоступных методах и т.д. Просто FWIW.

Ответ 2

Формально говоря, ваш коллега прав:

  • Если someMethod() или соответствующая трасса недостаточно горячая, интерпретируется байт-код и создается дополнительный стек стека

  • Если someMethod() вызывается на 9-м уровне глубины от горячей точки, вызовы requireNonNull() не должны быть встроены из-за MaxInlineLevel JVM Option

  • Если метод не вложен в какой-либо из вышеуказанных причин, аргумент T.J. Crowder вступает в игру, если вы используете конкатенацию для создания сообщения об ошибке

  • Даже если requireNonNull() встроен, JVM тратит время и пространство для выполнения этого.

С другой стороны, существует опция FreqInlineSize JVM, которая запрещает вложение слишком больших (в байткодах) методов. Байт-коды метода подсчитываются сами по себе, без учета размера методов, вызываемых внутри этого метода. Таким образом, извлечение фрагментов кода в независимые методы может быть полезно иногда, в примере с requireNonNull() это извлечение уже сделано для вас.

Ответ 3

Если вам нужны доказательства... тогда способ получить это - написать микро-бенчмарк.

(Я рекомендую сначала посмотреть проект Callper! Или JMH... по рекомендации Бориса. В любом случае, не пытайтесь писать микро-бенчмарк с нуля. Слишком много способов ошибиться.)

Однако вы можете сказать своему коллеге две вещи:

  • JIT-компилятор отлично справляется с наложением небольших вызовов методов, и вполне вероятно, что это произойдет в этом случае.

  • Если это не вызвало вызов, вероятность того, что разница в производительности будет всего лишь от 3 до 5 инструкций, маловероятно, что это будет иметь существенное значение.

Ответ 4

Ваш коллега, скорее всего, ошибается.

JVM очень умный и, скорее всего, встроит метод Objects.requireNonNull(...). Производительность сомнительна, но будет определенно гораздо более серьезная оптимизация, чем эта.

Вы должны использовать метод утилиты из JDK.

Ответ 5

Objects.requireNonNull более оптимизирован, как если бы вы это повторно использовали код. Также в oracle requireNonNull si определяется как

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
} 

так что он уже находится в байт-коде.