Поскольку мое исследование заставляет меня думать, что for
циклов – самая быстрая итерационная конструкция в PHP … чтобы сделать ее более ясной, какая из следующих, по вашему мнению, будет быстрее?
for ($i = 0; $i < count($myLargeArray); $i++ ) { echo myLargeArray[$i]; }
$count = count($myLargeArray); for ($i = 0; $i < $count; $i++ ) { echo myLargeArray[$i]; }
Из моей логики следует, что на каждой итерации в примере один доступ к длине myLargeArray на каждой итерации является более вычислительно дорогостоящим, чем доступ к простому целочисленному значению, как в примере 2. Это верно?
Первый способ медленнее, потому что функция count()
должна вызываться на каждой итерации цикла. Сам метод count()
довольно быстрый, но все еще есть некоторые накладные расходы при вызове функции вообще. Перемещая его за пределы цикла, вы выполняете так называемое движение цикла с циклическим инвариантом или иногда «поднимаете».
Существует целая серия оптимизаций , которые интересны для изучения.
Сказав все это, редко приходится об этом особо подчеркивать. В вашем примере здесь ввод-вывод повторения вывода, вероятно, в 10 раз больше, чем вы сохраняете в своей «оптимизации». И если вы делаете что-нибудь еще внутри своей петли, ваша оптимизация все меньше и меньше.
Я ненавижу быть мокрым одеялом, но для более чем 90% вашего кода производительность не является проблемой. Особенно, когда вы говорите о веб-приложениях, которые на начальном этапе составляют более 90% ввода-вывода.
Тем не менее, когда вы думаете, что ваш код виноват, вы должны:
Вы почти всегда обнаружите, что вам нужно улучшить свои стратегии кеширования и оптимизацию баз данных (это просто оптимизация ввода-вывода другим способом), вместо сводного кода.
Самая быстрая конструкция в этом случае на самом деле представляет собой цикл foreach:
foreach($myLargeArray as $element) { echo $element; }
Foreach () также хорош тем, что он всегда заканчивается, тогда как опечатка может оставить вас с бесконечным циклом, когда вы используете for ().
Пример 2. Не считайте элементы каждой итерацией.
Обновлено: мне только что сказали, что значение предварительно вычисляется: nNumOfElements specifies how many values are currently stored in the array. This is also the number that
nNumOfElements specifies how many values are currently stored in the array. This is also the number that
returns.
count ($ array) returns.
Мне кажется, функция count()
буквально ничего не делает, кроме потери нескольких микросекунд и тактовых циклов (для тех, кто знает ассемблер).
Читайте здесь: Общие сведения о реализации внутреннего массива PHP (исходный код PHP для разработчиков PHP – часть 4) .
Возможно, вы можете попробовать foreach range
:
foreach (range(0, (count(array)) as $number) { echo $number; }
Очевидно, что пример один медленнее. Условие $i < count($myLargeArray)
оценивается на каждой итерации, таким образом, подсчитывая массив несколько раз.
Проверьте этот и другие критерии на http://www.phpbench.com/
Изменить: они искали исходный код , и он был предварительно вычислен.
Однако время обработки тратится на эти многочисленные вызовы функций. Вот почему производительность падает. Массив «подсчитывается» несколько раз.
Самый быстрый цикл – это развернуть цикл. Некоторые редакторы кода, но не любые редакторы PHP, поддерживают это со специальным макросом, поэтому вам не нужно копировать и вставлять.
Поэтому я решил на самом деле количественно определить несколько вещей, в интересах получения некоторых реальных чисел. Вот базовый код, цикл, который строит большой массив из 100000 целых чисел.
$x = array(); for ($idx=0; $idx<100000; $idx++) $x[] = $idx;
Среднее время выполнения: 85 мс. Это включает время запуска PHP, анализ программы, запуск и выход. Теперь я добавляю еще один цикл, который выполняет итерацию через массив:
for ($idx=0; $idx<count($x); $idx++) { ; }
Среднее время выполнения: 105 мс. Когда вы вычитаете время установки 85 мс, вы можете видеть, что для повторения через массив из 100 000 элементов требуется всего 20 мс.
Теперь мы добавляем движение цикла с инвариантным циклом:
$m = count($x); for($idx=0; $idx<$m; $idx++) { ; }
Среднее время выполнения: 90 мс.
С одной стороны, эти сбережения огромны . Это 5 мс время итерации цикла вместо 20 мс. Поэтому вы можете утверждать, что это 75% экономии!
С другой стороны, это 15 мс. Меньше времени, чем большинство людей заметят на абсурдно большом массиве.
Но это массив, который ничего не делает . Посмотрим, что произойдет, когда мы выведем некоторые данные:
$m = count($x); for ($idx=0; $idx<$m; $idx++) { echo $idx; }
Теперь время выполнения составляет 200 мс. О, смотри, я только распечатывал индекс цикла. Я даже не выводил содержимое массива.
Это просто глупо. Давайте снова изменим программу, чтобы отобразить содержимое массива, а не только счетчик обращений:
$m = count($x); for ($idx=0; $idx<$m; $idx++) echo $x[$idx];
Новое время выполнения составляет 212 мс. Таким образом, потребовалось 5% дольше для доступа и эха содержимого массива, чем просто эхо счетчик циклов.
Давайте возьмем чье-то более раннее предложение и разворачиваем цикл. Я использовал это с большим успехом в C / C ++ в прошлом:
$m = count($x); for ($idx=0; $idx<$m; $idx+=5) { echo $x[$idx]; echo $x[$idx+1]; echo $x[$idx+2]; echo $x[$idx+3]; echo $x[$idx+4]; }
Сейчас мы говорим! Мы до 206 мс. О, подождите, это примерно 3% -ное улучшение для какого-то неудобного кода. И результат выглядит ужасно. Это просто строка чисел без пробелов или чего-то еще.
Давайте избавимся от разворачивания цикла и сделаем вывод немного приятнее:
$m = count($x); for ($idx=0; $idx<$m; $idx++) echo "{$x[$idx]}\n";
Время выполнения составляет 400 мс. Да. Это много дополнительного времени (относительно говоря), чтобы получить некоторое форматирование. Может быть, использование замены строк стоит нам что-то. Попробуем выполнить конкатенацию строк:
$m = count($x); for ($idx=0; $idx<$m; $idx++) echo $x[$idx] . "\n";
Новое время составляет 390 мс. Немного лучше. Попробуем разделить числа пробелом вместо новой строки:
$m = count($x); for ($idx=0; $idx<$m; $idx++) echo $x[$idx] . " ";
О, ничего себе, мы возвращаемся к 224 мс. Право на! Но что случилось? Ну, я запускаю все это на моем терминале Unix, и просто медленнее выводить числа на отдельных строках, чем выводить их на одной линии, которая обертывается.
Другими словами, скорость прокрутки терминальной программы оказывает большее влияние, чем все, что мы делали.