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



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



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



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



вот несколько ссылок sites:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php



то, что я слышу везде:





  • foreach медленно, и таким образом for/while быстрее

  • PHPs foreach копирует массив, который он повторяет; чтобы сделать его быстрее, вам нужно использовать ссылки

  • такой код: $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 is быстрее, чем foreach со ссылкой


  • foreach быстрее for


  • foreach быстрее for для хэш-таблице


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




  1. я делаю что-то неправильно?

  2. это PHP справочник по каждому элементу вещь действительно делает разницу? Я имею в виду, почему бы ему не скопировать его, если вы передаете по ссылке?

  3. каков эквивалентный код итератора для оператора foreach; я видел несколько сеть, но каждый раз, когда я их тестирую, время уходит; я также тестировал несколько простых конструкций итераторов, но никогда не получал даже приличных результатов-являются ли итераторы массива в PHP просто ужасными?

  4. существуют ли более быстрые способы/методы/конструкции для итерации через массив, отличный от FOR / FOREACH (и WHILE)?


PHP версии 5.3.0




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



  1. "я делаю что-то неправильно?" консенсус, кажется, таков: Да, я не могу использовать echo в тестах. Лично я все еще не вижу, как echo-это какая-то функция со случайным временем выполнения или как любая другая функция каким-то образом отличается-это и способность этого скрипта просто генерировать точно такие же результаты foreach лучше, чем все, трудно объяснить, хотя просто "вы используете echo" (Ну что я должен был быть с помощью.) Однако я допускаю, что тест должен быть сделан с чем-то лучшим; хотя идеальный компромисс не приходит на ум.


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


  3. " каков эквивалентный код итератора для оператора foreach; я видел несколько в сети, но каждый раз, когда я их тестирую, время уходит; я также тестировал несколько простых конструкций итератора, но никогда не получал даже приличных результатов-являются ли итераторы массива в PHP просто ужасными?" ircmaxell предоставил ответ ниже; хотя код может быть действителен только для версии PHP >= 5


  4. " существуют ли более быстрые способы/методы/конструкции для итерации через массив, отличный от FOR / FOREACH (и WHILE)?" спасибо Гордону за ответ. Использование новых типов данных в PHP5 должно дать либо повышение производительности, либо увеличение памяти (любой из которых может быть желательным в зависимости от вашей ситуации). В то время как скорость мудрый много новых типов массива, кажется, не лучше, чем array (), splpriorityqueue и splobjectstorage, кажется, значительно быстрее. Ссылка предоставлена Гордоном:http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/


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



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

801   4  

4 ответов:

мое личное мнение, что имеет смысл в контексте. Лично я почти никогда не пользуюсь for для обхода массива. Я использую его для других типов итераций, но 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();
}

как далеко поскольку существуют более быстрые способы итерации, это действительно зависит от проблемы. Но мне действительно нужно спросить, почему? Я понимаю, что хочу сделать вещи более эффективными, но я думаю, что вы тратите свое время на микро-оптимизацию. Помни,Premature Optimization Is The Root Of All Evil...

Edit: основываясь на комментарии, я решил сделать быстрый тестовый прогон...

$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 на самом деле делает на голом металле. Я скажу несколько вещей, которые будут правдой большую часть времени:

  1. Если вы не изменяете переменную, по-значению быстрее в PHP. Это потому, что это ссылка подсчитывается в любом случае и по значению дает ему меньше делать. Он знает, что во второй раз вы измените эту ZVAL (внутреннюю структуру данных PHP для большинства типов), ей придется сломать он отключается простым способом (скопируйте его и забудьте о другом ZVAL). Но вы никогда не изменяете его, так что это не имеет значения. Ссылки, что сложнее с учета в связи с знаю, что делать при изменении переменной. Поэтому, если вы только для чтения, парадоксально, что лучше не указывать на это с помощью &. Я знаю, это противоречит интуиции, но это также Правда.

  2. Foreach не медленный. И для простой итерации, условие это тестирование против - "я в конце этого массива" - выполняется с использованием собственного кода, а не PHP opcodes. Даже если это APC cached opcodes, это все еще медленнее, чем куча собственных операций, выполненных на голом металле.

  3. использование цикла for " for ($i=0; $i

  4. но даже если вы исправите это с помощью "$c=count ($x); for ($i=0; $i

  5. Как насчет старой школы " в то время как (список ("вещи? Ну, используя каждый (), текущий () и т. д. все собираются задействовать хотя бы 1 вызов функции, который не является медленным, но не бесплатным. Да, это снова PHP-коды! Так что в то время как + список + каждый имеет свои расходы, а также.

по этим причинам foreach по понятным причинам является лучшим вариантом для простой итерации.

и не забывайте, что это также Самый простой для чтения, так что это беспроигрышный вариант.

одна вещь, чтобы следить за в тестах (особенно 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

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

Comments

    Ничего не найдено.