Производительность FOR против FOREACH в PHP

Прежде всего, я понимаю, что в 90% приложений разница в производительности совершенно не имеет значения, но мне просто нужно знать, какая из них быстрее. Это и ...

Информация, доступная в настоящее время о них в сети, сбивает с толку. Многие люди говорят, что foreach - это плохо, но технически это должно быть быстрее, поскольку предполагается упростить написание массива с помощью итераторов. Итераторы, которые опять-таки должны быть более быстрыми, но в PHP, по-видимому, тоже очень медленные (или это не PHP)? Я говорю о функциях массива: next () prev () reset () и т. Д. Хорошо, если они являются даже функциями, а не одной из тех функций языка PHP, которые выглядят как функции.

Чтобы немного сузить это : я m не интересен для обхода массивов с шагами, превышающими 1 (отрицательных шагов тоже нет, т.е. обратная итерация). Я также не заинтересован в обходе и от произвольных точек, просто 0 к длине. Я также не вижу, что манипулирование массивами с более чем 1000 ключами происходит регулярно, но я вижу, что массив перебирается несколько раз в логике приложения! Также что касается операций, в основном только строковых манипуляций и эха.

Вот несколько справочных сайтов:
http://www.phpbench.com/
http://www.php.lt/benchmark /phpbench.php

What я слышу везде:

  • foreach медленный, и, таким образом, для / , в то время как быстрее
  • PHPs foreach копирует массив, по которому он повторяется; чтобы сделать это быстрее, вам нужно использовать код ссылки
  • следующим образом: $ key = array_keys ($ aHash); $ size = sizeOf ($ key);
    для ($ i = 0; $ 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 быстрее, чем для
  • , foreach быстрее, чем для для хеш-таблицы

Может кто-нибудь объяснить?

  1. Я что-то не так делаю?
  2. Действительно ли справка PHP foreach что-то меняет? Я имею в виду, почему бы не скопировать его, если вы передадите по ссылке?
  3. Что такое эквивалентный код итератора для оператора foreach; Я' видел несколько в сети, но каждый раз, когда я проверяю их, время отстает; Я также протестировал несколько простых конструкций итераторов, но никогда не получал даже приличных результатов - действительно ли итераторы массивов в PHP просто ужасны? (и WHILE)?

PHP версии 5.3.0


Редактировать: Ответить

С помощью людей здесь я смог собрать воедино ответы на все вопросы. Я суммирую их здесь:

  1. «Я что-то не так делаю?» Кажется, консенсус таков: да, я не могу использовать эхо в тестах. Лично я до сих пор не понимаю, как эхо - это какая-то функция со случайным временем выполнения или как любая другая функция как-то отличается - это и способность этого скрипта просто генерировать точно такие же результаты foreach лучше, чем все, трудно чтобы объяснить, хотя просто «вы используете эхо» (хорошо, что я должен был использовать). Тем не менее, я признаю, что тест должен быть сделан с чем-то лучшим; хотя идеальный компромисс не приходит на ум.
  2. «Действительно ли PHP foreach эталонная вещь действительно имеет значение? Я имею в виду, почему бы не копировать ее, если вы передадите по ссылке?» ircmaxell показывает, что да, это так, дальнейшее тестирование, кажется, доказывает, что в большинстве случаев ссылки должны быть быстрее - хотя, учитывая мой фрагмент кода выше, определенно не все. Я допускаю, что проблема, вероятно, слишком неинтуитивна, чтобы с ней беспокоиться на таком уровне, и потребовалось бы что-то экстремальное, такое как декомпиляция, чтобы фактически определить, что лучше для каждой ситуации.
  3. «Что такое эквивалентный код итератора для оператора foreach; Я видел несколько в сети, но каждый раз, когда я тестирую их, время отстает; я также тестировал несколько простых конструкций итераторов, но, кажется, никогда не получаю даже приличных результатов - итераторы массивов в PHP просто ужасны? " ircmaxell предоставил ответ ниже; хотя код может быть действительным только для версии PHP> = 5
  4. " Использование новых типов данных в PHP5 должно повысить производительность или память (любой из них может быть желателен в зависимости от вашей ситуации). В то время как по скорости многие новые типы массивов не кажутся лучше, чем array (), splpriorityqueue и splobjectstorage, по-видимому, значительно быстрее. Ссылка предоставлена ​​Гордоном: http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/

Спасибо всем, кто пытался помочь.

Я, скорее всего, буду придерживаться foreach (нереферентная версия) для любого простого обхода.

127
задан totallyNotLizards 10 December 2012 в 14:39
поделиться

2 ответа

Мое личное мнение - использовать то, что имеет смысл в контексте. Лично я почти никогда не использую вместо для обхода массива. Я использую его для других типов итераций, но foreach слишком прост ... Разница во времени в большинстве случаев будет минимальной.

Главное, на что следует обратить внимание:

for ($i = 0; $i < count($array); $i++) {

Это дорогой цикл, поскольку он вызывает счетчик на каждой итерации. Пока вы этого не делаете, я не думаю, что это действительно имеет значение ...

Что касается ссылки, имеющей значение, PHP использует копирование при записи, поэтому, если вы не записываете в массив , при зацикливании будет относительно мало накладных расходов. Однако, если вы начнете изменять массив внутри массива, вы начнете видеть различия между ними (так как нужно будет скопировать весь массив, а ссылка может просто изменить встроенный) ...

Что касается итераторы, 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();
}

Что касается более быстрых способов итерации, это действительно зависит от проблемы. Но мне действительно нужно спросить, почему? Я понимаю, что хочу сделать вещи более эффективными, но я думаю, что вы зря тратите время на микрооптимизацию. Помните, Преждевременная оптимизация - корень всего зла ...

Изменить: На основании комментария я решил провести быстрый тестовый прогон ...

$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), что ссылки значительно быстрее ...

104
ответ дан 24 November 2019 в 00:47
поделиться

Одна вещь, на которую следует обратить внимание в тестах (особенно на phpbench.com): даже если цифры верны, тесты - нет. Многие тесты на phpbench.com выполняют тривиальные задачи и злоупотребляют способностью PHP кэшировать поиск по массиву для искажения результатов тестов или в случае итерации по массиву фактически не тестируют его в реальных случаях (никто не пишет пустые для циклов). Я провел свои собственные тесты, которые, как я обнаружил, в значительной степени отражают результаты реального мира, и они всегда показывают собственный итерационный синтаксис языка foreach , выходящий на первое место (сюрприз, сюрприз ).

//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
30
ответ дан 24 November 2019 в 00:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: