Статьи,обзоры,справочники по PHP

       

Циклы: for, foreach, while, count/sizeof() - ускорение %-%


В начале программы создается массив $test из целых чисел (100 000 элементов). Потом один раз запускаются приведенные ниже примеры. Цикл проходит данный массив 3-мя способами (разными циклами) и выполняет кое-какие операции. Не выполнять в цикле ничего нельзя, ибо это будет уже совсем не реальный тест.

  1. {$x=0; foreach($test as $n)                          { $x=sprintf("test%08i",$i);        }}
  2. {$x=0; for ($it=0; $it<100000; $it++)                { $x=sprintf("test%08i",$i);        }}
  3. {$x=0; $it=0; while($it<100000)                      { $x=sprintf("test%08i",$i); $it++; }}
  4. {$x=0; for ($it=0; $it<count($test); $it++)          { $x=sprintf("test%08i",$i);        }}
  5. {$x=0; $it=0; while($it<count($test))                { $x=sprintf("test%08i",$i); $it++; }}
  6. {$x=0; $co=count($test); for ($it=0; $it<$co; $it++) { $x=sprintf("test%08i",$i);        }}
  7. {$x=0; $co=count($test); $it=0; while($it<$co)       { $x=sprintf("test%08i",$i); $it++; }}
  8. счетчик кол-во
    вызовов
    общее
    вpемя
    сpеднее
    вpемя
    % от min% от maxобщее
    время
    test N1112.031312.0313154.4%100.0%
    test N214.72904.729000.0%39.3%
    test N314.77124.771200.9%39.7%
    test N4110.284710.2847117.5%85.5%
    test N5110.346610.3466118.8%86.0%
    test N619.12719.127193.0%75.9%
    test N719.14099.140993.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 элементов:

    1. {$x=0; for ($it=0; $it<count($test); $it++)  { $x=sprintf("test%08i",$test[$it]);}}


    2. {$x=0; for ($it=0; $it<sizeof($test); $it++) { $x=sprintf("test%08i",$test[$it]);}}


    счетчик кол-во
    вызовов
    общее
    вpемя
    сpеднее
    вpемя
    % от min% от maxобщее
    время
    test N113.00873.008715.7%100.0%
    test N212.59982.599800.0%86.4% Пусть тесты будут иметь погрешности... Но результат один - count() заметно отстает по скорости от sizeof()! Хм, я бы к записи в мануале сделал приписку: "The sizeof() function is an alias for count(), but последний сильно тормозит!"



    Если кол-во элементов в массиве меньше 65000 (64К), то эти функции по скорости практически не различимы. Тут вывод простой - переходим на использование sizeof(), как ускоренного алиаса count(). Это принесет свои результаты на огромных массивах.

    Ассоциативные массивы: тестирование разных способов перебора

    С ними наблюдается таже проблема: на разных по величине массивах разные функции эффективны, но лучше всех foreach!

    Массив в 200 элементов и 1000 повторов программы:

    1. {$x=0; foreach($test as $k=>$v) { $x=sprintf("%s=>%s\n",$k,$v);                                                           }}


    2. {$x=0; reset($test); while (list($k, $v) = each($test)) { $x=sprintf("%s=>%s\n",$k,$v);                                   }}


    3. {$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]]); }}


    4. {$x=0; reset($test); while ($k=key($test)) { $x=sprintf("%s=>%s\n",$k,current($test)); next($test);                       }}


    счетчик кол-во
    вызовов
    общее
    вpемя
    сpеднее
    вpемя
    % от min% от maxобщее
    время
    test N118.12228.122200.0%78.7%
    test N2110.322110.322127.1%100.0%
    test N319.79219.792120.6%94.9%
    test N418.97118.971110.5%86.9% Тоже самое, но массив в 5000 элементов и 200 повторов:



    счетчик кол-во
    вызовов
    общее
    вpемя
    сpеднее
    вpемя
    % от min% от maxобщее
    время
    test N1114.447314.447300.0%67.2%
    test N2118.680118.680129.3%86.9%
    test N3121.505621.505648.9%100.0%
    test N4115.851415.851409.7%73.7% Опять тоже самое, но массив в 100 000 элементов и без повторов:

    счетчик кол-во
    вызовов
    общее
    вpемя
    сpеднее
    вpемя
    % от min% от maxобщее
    время
    test N113.51163.511600.0%82.8%
    test N213.97243.972413.1%93.6%
    test N314.24364.243620.8%100.0%
    test N414.00264.002614.0%94.3% Другие тесты на холостых циклах тоже показывают преимущество foreach.

    Резюме:


    • sizeof() лучше, чем count()
    • в циклах sizeof лучше вообще заменить на переменную
    • for и while практически не отличимы
    • для перебора простых индексных массивов нужно использовать for или while
    • для перебора ассоциативных массивов нужно использотьва foreach



    Содержание раздела