Объявить объект внутри или вне цикла? - PullRequest
55 голосов
/ 18 декабря 2008

Есть ли снижение производительности для следующего фрагмента кода?

for (int i=0; i<someValue; i++)
{
    Object o = someList.get(i);
    o.doSomething;
}

Или этот код на самом деле имеет больше смысла?

Object o;
for (int i=0; i<someValue; i++)
{
    o = someList.get(i);
    o.doSomething;
}

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

Ответы [ 16 ]

46 голосов
/ 18 декабря 2008

В современных компиляторах нет. Я объявляю объекты в наименьшей возможной области видимости, потому что это намного более читабельно для следующего парня.

15 голосов
/ 18 декабря 2008

Цитируя Кнута, который может цитировать Хоара :

Преждевременная оптимизация - корень всего зла.

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

Сравните это с количеством ошибок, которые вы, скорее всего, предотвратите, правильно указав переменную в объявлении цикла ...

10 голосов
/ 18 декабря 2008

Нет никакого снижения производительности за объявление объекта o в цикле. Компилятор генерирует очень похожий байт-код и делает правильные оптимизации.

См. Статью Миф. Определение переменных цикла внутри цикла отрицательно сказывается на производительности для аналогичного примера.

7 голосов
/ 18 декабря 2008

Вы можете разобрать код с помощью javap -c и проверить, что на самом деле выдает компилятор. На моей установке (java 1.5 / mac, скомпилированный с eclipse), байт-код цикла идентичен.

5 голосов
/ 18 декабря 2008

Первый код лучше, поскольку он ограничивает область действия переменной o блоком for. С точки зрения производительности, это может не иметь никаких эффектов в Java, но это может иметь место в компиляторах более низкого уровня. Они могут поместить переменную в регистр, если вы сделаете первый.

На самом деле, некоторые люди могут подумать, что если компилятор тупой, второй фрагмент лучше с точки зрения производительности. Это то, что какой-то преподаватель сказал мне в колледже, и я смеялся над ним за это предложение! По сути, компиляторы выделяют память в стеке для локальных переменных метода только один раз в начале метода (путем корректировки указателя стека) и освобождают ее в конце метода (снова путем корректировки указателя стека, предполагая, что это не C ++ или нет деструкторов, которые можно было бы назвать). Таким образом, все локальные переменные на основе стека в методе выделяются сразу, независимо от того, где они объявлены и сколько памяти им требуется. На самом деле, если компилятор тупой, нет никакой разницы с точки зрения производительности, но если он достаточно умен, первый код на самом деле может быть лучше, поскольку он поможет компилятору понять область действия и время жизни переменной! Кстати, если он действительно умный, то не должно быть абсолютно никакой разницы в производительности, поскольку он выводит реальную область действия.

Построение объекта с использованием new, конечно, полностью отличается от простого объявления его.

Я думаю, что читаемость важнее, чем производительность, и с точки зрения читабельности первый код определенно лучше.

4 голосов
/ 18 декабря 2008

Не преждевременно оптимизировать. Лучше, чем любой из них:

for(Object o : someList) {
    o.doSomething();
}

потому что это устраняет шаблон и проясняет намерения.

Если вы не работаете со встроенными системами, в этом случае все ставки отключены. В противном случае не пытайтесь перехитрить JVM.

4 голосов
/ 18 декабря 2008

Должен признать, я не знаю Java. Но эти два эквивалента? Время жизни объекта одинаково? В первом примере я предполагаю (не зная java), что o будет иметь право на сборку мусора сразу же после завершения цикла.

Но во втором примере, конечно, o не будет иметь право на сборку мусора, пока не будет завершена внешняя область (не показана)?

1 голос
/ 01 января 2009

Чтобы понять суть этого вопроса ... [Обратите внимание, что реализации не-JVM могут работать по-другому, если это разрешено JLS ...]

Во-первых, имейте в виду, что локальная переменная "o" в примере является указателем, а не фактическим объектом.

Все локальные переменные размещены в стеке времени выполнения в 4-байтовых слотах. двойные и длинные требуют двух слотов; другие примитивы и указатели берут один. (Даже логические выражения занимают полный слот)

Для каждого вызова метода должен быть создан фиксированный размер стека времени выполнения. Этот размер определяется максимальными локальными переменными "slots", необходимыми в любой данной точке метода.

В приведенном выше примере обе версии кода требуют одинакового максимального количества локальных переменных для метода.

В обоих случаях будет сгенерирован один и тот же байт-код, который обновит один и тот же слот в стеке времени выполнения.

Другими словами, вообще никакого снижения производительности.

ОДНАКО, в зависимости от остальной части кода в методе, версия «объявление вне цикла» может фактически потребовать большего выделения стека времени выполнения. Например, сравнить

for (...) { Object o = ... }
for (...) { Object o = ... }

с

Object o;
for (...) {  /* loop 1 */ }
for (...) { Object x =...; }

В первом примере оба цикла требуют одинакового выделения стека времени выполнения.

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

Надеюсь, это поможет, - Скотт

1 голос
/ 18 декабря 2008

Они имеют различную семантику. Что является более значимым?

Повторное использование объекта по «причинам производительности» часто неправильно.

Вопрос в том, что означает объект? ПОЧЕМУ ты это создаешь? Что это представляет? Объекты должны быть параллельны вещам реального мира. Вещи создаются, претерпевают изменения состояния и сообщают о своих состояниях по причинам.

Каковы эти причины? Как ваша объектная модель и отражает эти причины?

1 голос
/ 18 декабря 2008

Я всегда думал, что большинство компиляторов в наши дни достаточно умны, чтобы сделать последний вариант. Если предположить, что это так, то я бы сказал, что первый тоже выглядит лучше. Если цикл становится очень большим, нет необходимости искать, где объявлено o.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...