Прежде всего, я понимаю, что в 90% приложений разница в производительности совершенно не имеет значения, но мне просто нужно знать, какая из них быстрее. Это и …
Информация, доступная в настоящее время в сети, путается. Многие люди говорят, что foreach – это плохо, но технически это должно быть быстрее, поскольку предполагается упростить запись обхода массива с использованием итераторов. Итераторы, которые, опять же, предполагают, что они быстрее, но в PHP также, по-видимому, мертвы медленно (или это не проблема PHP?). Я говорю о функциях массива: next () prev () reset () и т. Д. Ну, если они четные функции, а не одна из тех функций языка PHP, которые выглядят как функции.
Чтобы сузить это немного : я не интересен в перемещении массивов с шагом всего на 1 (без отрицательных шагов, т. Е. С обратной итерацией). Меня также не интересует обход к и от произвольных точек, всего от 0 до длины. Я также не вижу манипуляции массивами с более чем 1000 ключами, происходящими на регулярной основе, но я вижу, что массив проходит несколько раз в логике приложения! Кроме того, что касается операций, в основном, только строковые манипуляции и эхо-сигналы.
Вот несколько ссылочных сайтов:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php
То, что я слышу везде:
foreach
медленный, и, следовательно, for
/ while
быстрее foreach
копирует массив, который он выполняет; чтобы сделать это быстрее, вам нужно использовать ссылки $key = array_keys($aHash); $size = sizeOf($key);
for ($i=0; $i < $size; $i++)
$key = array_keys($aHash); $size = sizeOf($key);
for ($i=0; $i < $size; $i++)
$key = array_keys($aHash); $size = sizeOf($key);
for ($i=0; $i < $size; $i++)
быстрее, чем foreach
Вот моя проблема. Я написал этот тестовый скрипт: http://pastebin.com/1ZgK07US и независимо от того, сколько раз я запускаю скрипт, я получаю что-то вроде этого:
foreach 1.1438131332397 foreach (using reference) 1.2919359207153 for 1.4262869358063 foreach (hash table) 1.5696921348572 for (hash table) 2.4778981208801
Вкратце:
foreach
быстрее, чем foreach
со ссылкой foreach
быстрее, чем for
foreach
быстрее, чем for
хэш-таблицы Может кто-нибудь объяснить?
PHP версия 5.3.0
Спасибо всем, кто пытался помочь.
Я, скорее всего, придерживаюсь foreach (версия без ссылки) для любого простого обхода.
Мое личное мнение – использовать то, что имеет смысл в контексте. Лично я почти никогда не использую for
обхода массива. Я использую его для других типов итераций, но foreach
слишком просто … В большинстве случаев разница во времени будет минимальной.
Самое главное наблюдать:
for ($i = 0; $i < count($array); $i++) {
Это дорогой цикл, поскольку он вызывает подсчет на каждую итерацию. Пока вы этого не делаете, я не думаю, что это действительно важно …
Что касается ссылки, которая делает разницу, PHP использует copy-on-write, поэтому, если вы не пишете массив, при циклизации будет относительно мало накладных расходов. Однако, если вы начнете изменять массив в массиве, вот где вы начнете видеть различия между ними (так как нужно будет скопировать весь массив, а ссылка может просто изменить встроенную) …
Что касается итераторов, foreach
эквивалентен:
$it->rewind(); while ($it->valid()) { $key = $it->key(); // If using the $key => $value syntax $value = $it->current(); // Contents of loop in here $it->next(); }
Поскольку существуют более быстрые способы повторения, это действительно зависит от проблемы. Но мне действительно нужно спросить, почему? Я понимаю, что хочу сделать вещи более эффективными, но я думаю, вы тратите свое время на микро-оптимизацию. Помните, Premature Optimization Is The Root Of All Evil
…
Редактировать: Основываясь на комментарии, я решил сделать быстрый тест …
$a = array(); for ($i = 0; $i < 10000; $i++) { $a[] = $i; } $start = microtime(true); foreach ($a as $k => $v) { $a[$k] = $v + 1; } echo "Completed in ", microtime(true) - $start, " Seconds\n"; $start = microtime(true); foreach ($a as $k => &$v) { $v = $v + 1; } echo "Completed in ", microtime(true) - $start, " Seconds\n"; $start = microtime(true); foreach ($a as $k => $v) {} echo "Completed in ", microtime(true) - $start, " Seconds\n"; $start = microtime(true); foreach ($a as $k => &$v) {} echo "Completed in ", microtime(true) - $start, " Seconds\n";
И результаты:
Completed in 0.0073502063751221 Seconds Completed in 0.0019769668579102 Seconds Completed in 0.0011849403381348 Seconds Completed in 0.00111985206604 Seconds
Поэтому, если вы изменяете массив в цикле, в несколько раз быстрее использовать ссылки …
И накладные расходы для просто ссылки на самом деле меньше, чем копирование массива (это на 5.3.2) … Таким образом, он появляется (по крайней мере, 5.3.2), как если бы ссылки значительно быстрее …
Я не уверен, что это так удивительно. Большинство людей, которые программируют на PHP, не очень разбираются в том, что на самом деле делает PHP на голом металле. Я расскажу несколько вещей, которые будут выполняться большую часть времени:
Если вы не изменяете эту переменную, то в PHP значение по-прежнему выполняется быстрее. Это связано с тем, что в любом случае эта ссылка считается подсчитанной, а ее значение дает меньше. Он знает, что во втором случае вы измените эту ZVAL (внутренняя структура данных PHP для большинства типов), она должна будет отключить ее простым способом (скопируйте ее и забудьте о другом ZVAL). Но вы никогда не изменяете его, так что это не имеет значения. Ссылки делают это более сложным с большей ведомостью, которую он должен делать, чтобы знать, что делать, когда вы изменяете переменную. Поэтому, если вы читаете только, как это ни парадоксально, лучше не говорить об этом с &. Я знаю, это противоречит интуиции, но это также верно.
Дело не медленное. И для простой итерации условие, которое оно тестирует, – «Я в конце этого массива» – выполняется с использованием собственного кода, а не кода PHP. Даже если это APC-кешированные коды операций, он все еще медленнее, чем куча собственных операций, выполненных на голой поверхности.
Использование цикла for for for ($ i = 0; $ i <count ($ x); $ i ++) является медленным из-за count () и отсутствия способности PHP (или действительно любого интерпретируемого языка) для оценки при анализе время, изменяет ли какой-либо элемент массив. Это не позволяет ему оценивать счет один раз.
Но даже после того, как вы исправите его с помощью «$ c = count ($ x)), для ($ i = 0; $ i <$ c; $ i ++) $ i <$ c – это куча опкодов Zend в лучшем случае, как и $ i ++. В ходе 100000 итераций это может иметь значение. Foreach знает на собственном уровне, что делать. Никаких PHP-кодов, необходимых для проверки состояния «я в конце этого массива».
А как насчет старой школы? While (list («stuff?» Ну, используя each (), current () и т. Д., Все будут задействованы как минимум один вызов функции, который не медленный, но не бесплатный. PHP opcodes снова! Так что, хотя + list + у каждого есть свои затраты.
По этим причинам foreach, по понятным причинам, является лучшим вариантом для простой итерации.
И не забывайте, что это также проще всего читать, поэтому это беспроигрышный.
Одна вещь, которой нужно следить в тестах (особенно phpbench.com), даже несмотря на то, что цифры звучат, тестов нет. Многие тесты на phpbench.com делают вещи в тривиальном режиме и злоупотребляют способностью PHP кэшировать поиск массива в искажениях тестов или, в случае итерации по массиву, фактически не проверяют его в реальных случаях (никто не пишет пусто для петли). Я сделал свои собственные тесты, которые, как я нашел, достаточно отражают результаты реального мира, и они всегда показывают собственный синтаксис языка для итерации, выходящий сверху (неожиданность, неожиданность).
//make a nicely random array $aHash1 = range( 0, 999999 ); $aHash2 = range( 0, 999999 ); shuffle( $aHash1 ); shuffle( $aHash2 ); $aHash = array_combine( $aHash1, $aHash2 ); $start1 = microtime(true); foreach($aHash as $key=>$val) $aHash[$key]++; $end1 = microtime(true); $start2 = microtime(true); while(list($key) = each($aHash)) $aHash[$key]++; $end2 = microtime(true); $start3 = microtime(true); $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++; $end3 = microtime(true); $start4 = microtime(true); foreach($aHash as &$val) $val++; $end4 = microtime(true); echo "foreach ".($end1 - $start1)."\n"; //foreach 0.947947025299 echo "while ".($end2 - $start2)."\n"; //while 0.847212076187 echo "for ".($end3 - $start3)."\n"; //for 0.439476966858 echo "foreach ref ".($end4 - $start4)."\n"; //foreach ref 0.0886030197144 //For these tests we MUST do an array lookup, //since that is normally the *point* of iteration //i'm also calling noop on it so that PHP doesn't //optimize out the loopup. function noop( $value ) {} //Create an array of increasing indexes, w/ random values $bHash = range( 0, 999999 ); shuffle( $bHash ); $bstart1 = microtime(true); for($i = 0; $i < 1000000; ++$i) noop( $bHash[$i] ); $bend1 = microtime(true); $bstart2 = microtime(true); $i = 0; while($i < 1000000) { noop( $bHash[$i] ); ++$i; } $bend2 = microtime(true); $bstart3 = microtime(true); foreach( $bHash as $value ) { noop( $value ); } $bend3 = microtime(true); echo "for ".($bend1 - $bstart1)."\n"; //for 0.397135972977 echo "while ".($bend2 - $bstart2)."\n"; //while 0.364789962769 echo "foreach ".($bend3 - $bstart3)."\n"; //foreach 0.346374034882
Я думаю, но я не уверен: цикл for
выполняет две операции для проверки и увеличения значений. foreach
загружает данные в память, тогда он будет перебирать все значения.