Циклы: for, foreach, while, count/sizeof() – ускорение 15%-30%
В начале программы создается массив $test из целых чисел (100 000 элементов). Потом один раз запускаются приведенные ниже примеры. Цикл проходит данный массив 3-мя способами (разными циклами) и выполняет кое-какие операции. Не выполнять в цикле ничего нельзя, ибо это будет уже совсем не реальный тест.
- {$x=0; foreach($test as $n) { $x=sprintf("test%08i",$i); }}
- {$x=0; for ($it=0; $it<100000; $it++) { $x=sprintf("test%08i",$i); }}
- {$x=0; $it=0; while($it<100000) { $x=sprintf("test%08i",$i); $it++; }}
- {$x=0; for ($it=0; $it<count($test); $it++) { $x=sprintf("test%08i",$i); }}
- {$x=0; $it=0; while($it<count($test)) { $x=sprintf("test%08i",$i); $it++; }}
- {$x=0; $co=count($test); for ($it=0; $it<$co; $it++) { $x=sprintf("test%08i",$i); }}
- {$x=0; $co=count($test); $it=0; while($it<$co) { $x=sprintf("test%08i",$i); $it++; }}
счетчик | кол-во вызовов |
общее вpемя |
сpеднее вpемя |
% от min | % от max | |
test N1 | 1 | 12.0313 | 12.0313 | 154.4% | 100.0% | |
test N2 | 1 | 4.7290 | 4.7290 | 00.0% | 39.3% | |
test N3 | 1 | 4.7712 | 4.7712 | 00.9% | 39.7% | |
test N4 | 1 | 10.2847 | 10.2847 | 117.5% | 85.5% | |
test N5 | 1 | 10.3466 | 10.3466 | 118.8% | 86.0% | |
test N6 | 1 | 9.1271 | 9.1271 | 93.0% | 75.9% | |
test N7 | 1 | 9.1409 | 9.1409 | 93.3% | 76.0% |
Почему sprintf, а не реальное echo? echo использовать нельзя, т.к. от него будет немерянный буфер (OUTPUT в браузер или консоль).
Теперь о деле. Бесспорный вывод – использование foreach сильно тормозит дело, а между for и while большой разницы нет. (На голом тесте for/while/foreach {..}тормоза foreach – 30%). Это не удивительно, т.к. foreach делает копию массива, на что тратиться масса времени (хотя это только слухи).
Вывод с count() не столь очевиден, потому что от разного текста в цикле % тормознутости от самого быстрого варианта резко возрастает… Я взял цикл с небольшой нагрузкой – проход по огромному массиву $test + форматирование функцией sprintf. Как видите, варинты с count() и заменяющей эту функцию перемнной $co различаются на 10% по скорости между собой (не смотрите на варинант с константой в 100000, заранее знать кол-во элементов невозможно).
Вывод о не ассоциативных массивах: 1) foreach существенно замедляет работу 2) использование count() в простых циклах – замедленение 10%. Но на сложных циклах потери от лишних запусков count() будут абсолютно незаметны, так что ситуация не очевидна.
Сравнение count() и sizeof().
Судя по мануалу – это алиасы. Об этом написано на страницах самих функций и дополнительной странице “Appendex => Aliases list”. Что же мы видим на массиве в 100000 элементов:
- {$x=0; for ($it=0; $it<count($test); $it++) { $x=sprintf("test%08i",$test[$it]);}}
- {$x=0; for ($it=0; $it<sizeof($test); $it++) { $x=sprintf("test%08i",$test[$it]);}}
счетчик | кол-во вызовов |
общее вpемя |
сpеднее вpемя |
% от min | % от max | |
test N1 | 1 | 3.0087 | 3.0087 | 15.7% | 100.0% | |
test N2 | 1 | 2.5998 | 2.5998 | 00.0% | 86.4% |
Пусть тесты будут иметь погрешности… Но результат один – count() заметно отстает по скорости от sizeof()! Хм, я бы к записи в мануале сделал приписку: “The sizeof() function is an alias for count(), but последний сильно тормозит!”
Если кол-во элементов в массиве меньше 65000 (64К), то эти функции по скорости практически не различимы. Тут вывод простой – переходим на использованиеsizeof(), как ускоренного алиаса count(). Это принесет свои результаты на огромных массивах.
Ассоциативные массивы: тестирование разных способов перебора
С ними наблюдается таже проблема: на разных по величине массивах разные функции эффективны, но лучше всех foreach!
Массив в 200 элементов и 1000 повторов программы:
- {$x=0; foreach($test as $k=>$v) { $x=sprintf("%s=>%s\n",$k,$v); }}
- {$x=0; reset($test); while (list($k, $v) = each($test)) { $x=sprintf("%s=>%s\n",$k,$v); }}
- {$x=0; $k=array_keys($test); $co=sizeof($k); for ($it=0; $it<$co; $it++) { $x=sprintf("%s=>%s\n",$k[$it],$test[$k[$it]]); }}
- {$x=0; reset($test); while ($k=key($test)) { $x=sprintf("%s=>%s\n",$k,current($test)); next($test); }}
счетчик | кол-во вызовов |
общее вpемя |
сpеднее вpемя |
% от min | % от max | |
test N1 | 1 | 8.1222 | 8.1222 | 00.0% | 78.7% | |
test N2 | 1 | 10.3221 | 10.3221 | 27.1% | 100.0% | |
test N3 | 1 | 9.7921 | 9.7921 | 20.6% | 94.9% | |
test N4 | 1 | 8.9711 | 8.9711 | 10.5% | 86.9% |
Тоже самое, но массив в 5000 элементов и 200 повторов:
счетчик | кол-во вызовов |
общее вpемя |
сpеднее вpемя |
% от min | % от max | |
test N1 | 1 | 14.4473 | 14.4473 | 00.0% | 67.2% | |
test N2 | 1 | 18.6801 | 18.6801 | 29.3% | 86.9% | |
test N3 | 1 | 21.5056 | 21.5056 | 48.9% | 100.0% | |
test N4 | 1 | 15.8514 | 15.8514 | 09.7% | 73.7% |
Опять тоже самое, но массив в 100 000 элементов и без повторов:
счетчик | кол-во вызовов |
общее вpемя |
сpеднее вpемя |
% от min | % от max | |
test N1 | 1 | 3.5116 | 3.5116 | 00.0% | 82.8% | |
test N2 | 1 | 3.9724 | 3.9724 | 13.1% | 93.6% | |
test N3 | 1 | 4.2436 | 4.2436 | 20.8% | 100.0% | |
test N4 | 1 | 4.0026 | 4.0026 | 14.0% | 94.3% |
Другие тесты на холостых циклах тоже показывают преимущество foreach.
Резюме:
- sizeof() лучше, чем count()
- в циклах sizeof лучше вообще заменить на переменную
- for и while практически не отличимы
- для перебора простых индексных массивов нужно использовать for или while
- для перебора ассоциативных массивов нужно использотьва foreach