Циклы: 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 делает копию массива, на что тратиться масса времени (хотя это только слухи). Вывод о не ассоциативных массивах: 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 the last is very slow!" Если кол-во элементов в массиве меньше 65000 (64К), то эти функции по скорости практически не различимы. Тут вывод простой - переходим на использование sizeof(), как ускоренного алиаса count(). Это принесет свои результаты на огромных массивах. Ассоциативные массивы: тестирование разных способов перебора С ними наблюдается такая же проблема: на разных по величине массивах разные функции эффективны, но лучше всех foreach! Массив в 200 элементов и 1000 повторов программы: {$x=0; foreach($test as $k=>$v) { $x=sprintf("%s=>%s
",$k,$v); }} {$x=0; reset($test); while (list($k, $v) = each($test)) { $x=sprintf("%s=>%s
",$k,$v); }} {$x=0; $k=array_keys($test); $co=sizeof($k); for ($it=0; $it<$co; $it++) { $x=sprintf("%s=>%s
",$k[$it],$test[$k[$it]]); }} {$x=0; reset($test); while ($k=key($test)) { $x=sprintf("%s=>%s
",$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 Для чтения файла file() быстрее, чем fopen+цикл - ускорение 40% Чтобы прочитать в массив $x файл размером 1Мб (100 000 строк по 10 байт) можно воспользоваться двумя вариантами: чтение файла с помощью file(), либо традиционным методом fopen/fgets. Разумеется, для файлов разного объема и содержимого скорость может меняться. Но в данном примере статистика такова: file("1Mb_file.txt") работает на 40% быстрее, чем: $f=fopen("1Mb_file.txt","r") or die(1); while($x[]=fgets($f,1000)); fclose($f);
Аналогичные варианты: $f=fopen("1Mb_file.txt","r") or die(1); while($s=fgets($f,1000)) $x[]=$s; fclose($f);
или $f=fopen("1Mb_file.txt","r") or die(1); while(!feof($f))) $x[]=fgets($f,1000); fclose($f); работают еще медленнее (во втором случае лишняя функция feof() заметно снижает скорость). Тот же тест, но на 15Мб файле (100 000 строк по 150 байт) показывает разницу в 50%, в пользу file(). Тест проводился так, чтобы исключить фоновый своппинг во время работы из-за предшествующих команд создания/чтения таких больших файлов. Подсчитать тоже самое на очень маленьких файлах в 1-2 Кб не представляется возможным, т.к. операцию чтения нельзя повторять в течении одного теста, операции чтения будут кешироваться... |