Использование try-finally для выполнения операторов после возврата
Рассмотрим следующий код:
Foo result = array[index];
index = (index + 1) % array.length;
return result;
Для выполнения некоторых заключительных действий требуется дополнительная переменная. Имеет ли смысл написать это как:
try {
return array[index];
} finally {
index = (index + 1) % array.length;
}
или это повлияет на производительность? Обычно это считается хорошей / плохой практикой, и если да, то почему?
(В примере предполагается, что index
действительный индекс для array
и код не будет бросать ArrayIndexOutOfBoundsException
)
Редактировать: вопрос не о необходимости использования try-finally
а скорее о любом выигрыше или потере в производительности, которые я получаю, решив сделать это. Без этого переменная создается. С его помощью возвращаемое значение сохраняется где-то еще, возможно, более эффективным способом.
2 ответа
Без finally
Вы объявляете действительно дополнительный Foo
переменная.
Но действительно ли это дорого? Не так, как в обоих случаях Foo
объект существует в памяти. Вы только что добавили ссылку для доступа к нему.
Ссылка на объект в области действия метода действительно дешева.
Вы никогда не должны беспокоиться об этом.
Кроме того, вам не нужно использовать finally
оператор как способ повышения производительности исполняемого кода.
Читатели кода никогда не догадаются об этом.finally
служит для:
Блок finally всегда выполняется при выходе из блока try. Это гарантирует, что блок finally выполняется даже в случае непредвиденного исключения.
а также
размещение кода очистки в блоке finally - это всегда хорошая практика, даже если исключений не ожидается.
Первый код без finally
утверждение намного яснее и не имеет никакого косвенного чтения.
Поэтому советую придерживаться:
Foo result = array[index];
index = (index + 1) % array.length;
return result;
Как прокомментировано, основными накладными расходами является использование %
вместо условия или маски
Вы можете запустить тест с JMH
static class Foo {
}
Foo[] array = new Foo[8];
int index = 0;
@Benchmark
public Foo simple() {
Foo result = array[index];
index = (index + 1) % array.length;
return result;
}
@Benchmark
public Foo withFinally() {
try {
return array[index];
} finally {
index = (index + 1) % array.length;
}
}
@Benchmark
public Foo withCondition() {
int i = index++;
if (index == array.length) index = 0;
return array[i];
}
@Benchmark
public Foo withMask() {
int i = index++;
return array[i & (array.length-1)];
}
Результаты на моей машине... Ваш пробег будет меняться
Benchmark Mode Cnt Score Error Units
ModMain.simple thrpt 25 132.473 ± 1.764 ops/us
ModMain.withCondition thrpt 25 363.077 ± 4.752 ops/us
ModMain.withFinally thrpt 25 130.179 ± 1.585 ops/us
ModMain.withMask thrpt 25 397.310 ± 3.506 ops/us
Чем выше, тем лучше.
Короче говоря, используя finally
может быть немного медленнее, но я не буду беспокоиться об этом по сравнению с альтернативами.