BVIsoft.com - документация, статьи и примеры по PHP. [Назад]

Быстрая оптимизация PHP-скриптов (часть 2)[Печать]
Разместил: admin . Раздел: Общие. Опубликовано: 07-25-2007 21:50:15

Циклы: 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 Кб не представляется возможным, т.к. операцию чтения нельзя повторять в течении одного теста, операции чтения будут кешироваться...

Источник: http://www.softportal.com/   Прочитана 763 раз.