Простая проблема производительности цикла - PullRequest
2 голосов
/ 29 ноября 2011

Скажем, у меня есть простой цикл PHP, подобный этому

// Bad example
$array = array('apple','banana','cucumber');
for ($i = 1; $i < count($array); $i++) {
    echo $array[$i];
}

Я знаю, что это плохая практика.Лучше не , используя count() внутри цикла.

// Nice example
$array = array('apple','banana','cucumber');
$limit = count($array);
for ($i = 1; $i < $limit; $i++) {
    // do something...
}    

На Java я бы сделал это следующим образом

// Bad example?
String[] array = {"apple","banana","cucumber"};
for(int i = 0; i < array.length; i++){
    System.out.println(array[i]);
}

Вопрос: Разве это тоже не плохая практика?Или это так же, как в примере ниже?

// Nice example?
String[] array = {"apple","banana","cucumber"};
int limit = array.length;
for(int i = 0; i < limit; i++){
    System.out.println(array[i]);
}

Ответы [ 5 ]

6 голосов
/ 29 ноября 2011

Любой приличный компилятор / интерпретатор должен автоматически оптимизировать первый пример, чтобы он соответствовал второму (в любом случае семантически говоря, если не совсем буквально), и, вероятно, третий, чтобы соответствовать четвертому.Он известен как инвариантная оптимизация цикла , где компилятор распознает, что объект (переменная, выражение и т. Д.) Не изменяется внутри цикла (т. Е. Является инвариантом ), и удаляет его извнепетля (свободно говоря).

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

5 голосов
/ 29 ноября 2011

«Плохие» примеры, которые вы используете, не эквивалентны и, следовательно, не сопоставимы - даже если они кажутся таковыми на поверхности. Используя это описание:

for (initialization; termination; increment) {
    statement(s)
}

(который описывает как циклы PHP, так и java), оператор initialization выполняется один раз, в начале цикла. Оператор termination и шаг выполняются для каждой итерации цикла.

Причиной плохой практики использования PHP count в операторе terminating является то, что для каждой итерации происходит вызов функции count. В вашем примере с Java array.length - это не вызов функции, а ссылка на открытый член. Следовательно, операторы terminating , используемые в ваших примерах, не являются эквивалентным поведением. Мы ожидаем, что вызов функции будет более дорогостоящим, чем ссылка на свойство.

Неправильно размещать вызов функции (или вызывать свойство, маскирующее функцию) в операторе terminating цикла for в любом языке , который имеет описанный цикл механика. Вот что делает пример PHP «плохим», и было бы одинаково плохо, если бы вы использовали функцию count -типа в Java для оператора завершения цикла. Таким образом, реальный вопрос заключается в том, действительно ли Java Array.length маскирует вызов функции - ответом на него является «нет» (см. Потенциальный повторяющийся вопрос и / или проверьте http://leepoint.net/notes-java/data/arrays/arrays.html)

2 голосов
/ 29 ноября 2011

Основное отличие состоит в том, что count() является функцией, тогда как array.length является свойством и, следовательно, не отличается от переменной limit.

1 голос
/ 29 ноября 2011

В Java это не имеет значения, что массив имеет этот атрибут как константу (public final int). Разница заключается в том, что Java-массивы имеют фиксированный размер и не могут расти, поэтому нет необходимости каждый раз считать элементы для доступа к длине.

1 голос
/ 29 ноября 2011

Они не одинаковы, в "хорошем примере" Java вы не каждый раз вычисляете длину массива. Вместо этого вы сохраняете это в переменной limit и используете его для остановки вычисления вместо результата вызова функции length для массива на каждой итерации цикла for.

РЕДАКТИРОВАТЬ: обе вещи, которые вы считали "плохой практикой", являются плохой практикой, а "хорошие примеры" - более эффективными способами (по крайней мере, в теории). Но это правда, что в реализации не будет заметной разницы.

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