Оптимизация РНР-сценариев. Часть 5. Инвариантная оптимизация циклов

Инвариантная оптимизация циклов

В любом языке программирования цикл является одним из самых главных инструментов. Однако использование циклов может привести к заметному замедлению выполнения кода. Чтобы продемонстрировать это на примере, рассмотрим сценарий, который принимает строку и создает новый перетасованный вариант этой строки.

Один из вариантов решения этой задачи показан ниже.

$shuffled = array();
for($i = 0; $i < (strlen($string)-l); $i++) {
  $shuffled[] = $string[rand(0, (strlen($string)-1))];
}
$new_string = implode($shuffled);

Обратите внимание на то, что в этом примере для каждой итерации цикла for вызывается функция strlen (). В данном случае возвращаемым значением функции strlen () является константа для этого цикла (инвариант), которая требует только одного вычисления. Метод #2 убирает вычисление функции strlen () из цикла, вычисляя значение один раз и присваивая результат переменной:

$str_len = strlen($string) -1 ;
$shuffled = array();
for($i = 0; $i < $str_len; $i++) {
  $shuffled[] = $string[rand(0, $str_len)];
}
$new_string = implode($shuffled) ;

Если сравнить время выполнения этих двух методов, будет получен следующий результат:

Метод #1 занял 0.04446005821228 секунд.
Метод #2 занял 0.035489916801453 секунд.
Метод #2 был быстрее метода #1 на 20.18%

НА ЗАМЕТКУ
В отношении этого фрагмента кода следует сказать, что количество времени, потраченное
на выполнение сегмента кода, было столь малым, что полученный результат оказался не-
точным. Чтобы получить более точные данные, методы пришлось выполнять по 100 раз.

Как видите, иногда достаточно удалить из цикла вызов инвариантной функции,
чтобы повысить производительность на 20%. Более того, невозможность обнаружения и удаления инвариантов из циклов может существенно снизить производительность приложения (особенно если они присутствуют в нескольких различных частях кода).
В данном случае инвариантное значение было вызвано для функции strlen (). Однако почти всегда оптимизировать нужно любое не скалярное значение, используемое внутри цикла. Далее приводится самый распространенный пример организации цикла с использованием значения массива (предполагая, что все переменные определяются соответствующим образом):

$myarray['myvalue'] = 1000001;
for($i = 0; $i < $myarray['myvalue']; $i++) {
  $count++;
}

Хотя функция и не вызывается, однако при каждом обращении к массиву $myarray необходимо выполнять поиск в хэш-таблице внутри цикла. Это гораздо медленнее, чем время, необходимое для обращения к скалярным значениям:

$myarray['myvalue'] = 1000001;
$myscalar = $myarray['myvalue1] ;
for($i = 0; $i < $myscalar; $i++) {
  $count++;
}

Если сравнить оба метода, получим следующий результат:
Метод #1 занял 3.676020026208 секунд.
Метод #2 занял 2.6184829473496 секунд.
Метод #2 был быстрее метода #1 на 28.77%
Как видите, почти 30-процентное повышение производительности достигается путем присваивания инвариантного значения массива скаляру во время работы цикла (это даже еще более впечатляющий вариант, чем при первоначальной оптимизации strlen ()).

Так-же полезно ознакомиться как в языке php удалить элемент массива?

  • А я считаю, что все это верно и очень точно подмечено! И таких мелочей можно накопать тысячу.

  • кстати автору хочу предложить установить от яндекс.денег

  • Евгений

    Отличная статья Спасибо огромное

  • natio

    Отличная статья Спасибо огромное

  • Алина

    Жаль, что в Интернете мало находила таких содержательных материалов

  • WP

    Спасибо за Ваш труд, и хорошую статью!!

  • Рыбка

    Отлично написано! Буду думать…
    Надо бы и самой заняться оптимизацией скриптов на сайте.