Является ли 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
?
Ответы
Ответ 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;
}
так что он уже находится в байт-коде.