Одним из основных критериев успешности любого интернет-ресурса является скорость его работы и с каждым годом пользователи становятся всё более и более требовательными по этому критерию. Оптимизация работы php-скиптов — это один из методов обеспечения скорости работы системы.
В этой статье я бы хотел представить на суд общественности свой сборник советов и фактов по оптимизации скриптов. Сборник собирался мною достаточно долго, основан на нескольких источниках и личных экспериментах.
Почему сборник советов и фактов, а не жестких правил? Потому что, как я убедился, не существует «абсолютно правильной оптимизации». Многие приёмы и правила противоречивы и выполнить их все невозможно. Приходиться выбирать совокупность методов, которыми приемлемо пользоваться без ущерба безопасности и удобности. Я занял рекомендательную позицию и поэтому у меня советы и факты, которые Вы можете соблюдать, а можете и не соблюдать.
Что бы не было путаницы, я разделил все советы и факты на 3 группы:
- Оптимизация на уровне логики и организации приложения
- Оптимизация кода
- Бесполезная оптимизация
Группы выделены условно и некоторые пункты можно отнести сразу к нескольким из них. Цифры приведены для среднестатистического сервера (LAMP). В статье не рассматриваются вопросы связанные с эффективностью различных сторонних технологий и фреймворков, так как это тема отдельных дискуссий.
Оптимизация на уровне логики и организации приложения
Многие из советов и фактов, относящихся к данной группе оптимизации, являются весьма значимыми и дают весьма большой выигрыш во времени.
- Постоянно профилируйте свой код на сервере (
xdebug
) и на клиенте (firebug
), что бы выявить узкие места кода
Следует отметить, что профилировать надо и серверную, и клиентскую часть, так как не все серверные ошибки можно обнаружить на самом сервере. - Количество, используемых в программе пользовательских функций, никак не влияет на скорость
Это позволяет использовать в программе бесчисленное число пользовательских функций. - Активно используйте пользовательские функций
Положительный эффект достигается за счёт того, что внутри функций операции ведутся только с локальными переменными. Эффект этого больше, чем расходы на вызовы пользовательских функций. - «Критически тяжёлые» функции желательно реализовать на стороннем языка программирования в виде расширения PHP
Это требует навыков программирования на стороннем языке, что значительно увеличивает время разработки, но в тоже время позволяет использовать приёмы вне возможности PHP. - Обработка статического файла html быстрее, чем интерпретируемого файла php
Различие повремени на клиенте может составлять около 1 секунды, поэтому имеет смысл четкое разделение статики и генерируемых средствами PHP страниц. - Размер обрабатываемого (подключаемого) файла влияет на скорость
Примерно на обработку каждых 2 Кб тратиться 0.001 секунды. Этот факт толкает нас на проведение минимизации кода скриптов при перенесении на рабочий сервер. - Старайтесь не использовать постоянно
require_once
илиinclude_once
Эти функции нужно использовать при наличии возможности повторного считывания файла, в остальных случаях желательно использоватьrequire
иinclude
. - При ветвлении алгоритма, если имеются конструкции, которые могут не обрабатываться и их объём порядка 4 Кб и более, то более оптимально их подключать при помощи
include.
- Желательно использовать проверку отправляемых данных на клиенте
Это вызвано тем, что при проверке данных на стороне клиента, резко снижается количество запросов с неверными данными. Системы проверки данных на клиенте строятся в основном с использованием JS и жестких элементов формы (select). - Желательно большие конструкций DOM для массивов данных строить на клиенте
Это очень эффективный метод оптимизации при работе с отображением большого объёма данных. Суть его сводится к следующему: на сервере подготавливается массив данных и передаётся клиенту, а построение конструкций DOM предоставляется JS функциям. В результате нагрузка частично перераспределяется с сервера на клиент. - Системы, построенные на технологии AJAX, значительно быстрее, чем системы, не использующие эту технологию
Это вызвано уменьшением объёмов вывода и перераспределением нагрузки на клиента. На практике скорость систем с AJAX в 2-3 раза выше. Замечание: AJAX в свою очередь создаёт ряд ограничений на использование других методов оптимизации, например, работа с буфером. - При получение post-запроса всегда возвращайте что-нибудь, можно даже пробел
Иначе клиенту будет отправлена страница ошибки, которая весит несколько килобайт. Данная ошибка очень часто встречается в системах, использующих технологию AJAX. - Получение данных из файла быстрее, чем из БД
Это во многом вызвано затратами на подключение к БД. К моему удивлению, огромный процент программистов маниакально хранят все данные в БД, даже когда использование файлов быстрее и удобнее. Замечание: в файлах можно хранить данные, по которым не ведётся поиск, в противном случае следует использовать БД. - Не осуществляйте подключение к БД без необходимости
По неизвестной мне причине, многие программисты осуществляют подключение к БД на этапе считывания настроек, хотя далее они могут не делать запросов к БД. Это вредная привычка, которая стоит в среднем 0.002 секунды. - Используйте постоянное соединение с БД при малом количестве одновременно активных клиентов
Выгода во времени вызвана отсутствием затрат на подключение к БД. Разница во времени примерно 0.002 секунды. Замечание: при большом количестве пользователей постоянные соединения использовать нежелательно. При работе с постоянными соединениями должен быть механизм завершения соединений. - Использование сложных запросов к БД быстрее, чем использование нескольких простых
Разница во времени зависит от многих факторов (объём данных, настройка БД и пр.) и измеряется тысячными, а иногда даже сотыми, секунды. - Использование вычислений на стороне СУБД быстрее, чем вычисления на стороне PHP для данных хранящихся в БД
Это вызвано тем фактором, что для таких вычислений на стороне PHP требуется два запроса к БД (получение и изменение данных). Разница во времени зависит от многих факторов (объём данных, настройка БД и пр.) и измеряется тысячными и сотыми секунды. - Если данные выборки из БД редко меняются и к этим данным обращается множество пользователей, то имеет смысл сохранить данные выборки в файл
Например можно использовать следующий простой подход: получаем данные выборки из БД и сохраняем их как сериализованный массив в файл, далее любой пользователь использует данные из файла. На практике такой метод оптимизации может дать многократный прирост скорости выполнения скрипта. Замечание: При использовании данного метода требуются писать инструменты для формирования и изменения данных хранимых файле. - Кэшируйте данные, которые редко меняются, при помощи memcached
Выигрыш времени может быть весьма значительным. Замечание: кэширование эффективно для статичных данных, для динамичных данных эффект снижается и может быть отрицательным. - Работа без объектов (без ООП) быстрее, чем работа с использованием объектов, примерно, в три раза
Памяти «съедается» также больше. К сожалению, интерпретатор PHP не может работать с ООП также быстро как с обычными функциями. - Чем больше мерность массивов, тем медленнее они работают
Потеря времени возникает из-за обработки вложенности структур.
Оптимизация кода
Данные советы и факты дают незначительны по сравнению с предыдущей группой прирост скорости, но в своей совокупности эти приёмы могут дать неплохой выигрыш времени.
echo
иprint
значительно быстрее, чемprintf
Разница во времени может доходить до нескольких тысячных секунды. Это вызвано тем, чтоprintf
служит для вывода форматированных данных и интерпретатор проверяет полностью строку на вхождение таких данных.printf
используется только для вывода данных, которым нужно форматирование.echo $var."text"
быстрее, чемecho "$var text"
Это вызвано тем, что движок PHP во втором случае вынужден искать переменные внутри строки. Для больших объёмов данных и старых версий PHP различия по времени заметны.echo 'a'
быстрее, чемecho "a"
для строк без переменных
Это вызвано тем, что во втором случае движок PHP пытается найти переменные. Для больших объёмов данных различия во времени достаточно заметны.echo 'a','b'
быстрее, чемecho 'a'.'b'
Вывод данных через запятую быстрее, чем через точку. Это вызвано тем, что во втором случае происходит конкатенация строк. Для больших объёмов данных различия во времени достаточно заметны. Примечание: это работает только с функцией echo, которая может принимать несколько строк в качестве аргументов.$return='a'; $return.='b'; echo $return;
быстрее, чемecho 'a'; echo 'b';
Причина в том, что вывод данных требует некоторых дополнительных операций. Для больших объёмов данных различия во времени достаточно заметны.ob_start(); echo 'a'; echo 'b'; ob_end_flush();
быстрее, чем$return='a'; $return.='b'; echo $return;
Это вызвано тем, что вся работа осуществляется без обращения к переменным. Для больших объёмов данных различия во времени достаточно заметны. Замечание: данный прием неэффективен, если вы работаете с AJAX, так как в этом случае данные желательно возвращать в виде одной строки.- Используйте «профессиональную вставку» или
?> a b <?php
быстрее, чем<?php echo 'a'; echo 'b'; ?>
Статические данные (вне программного кода) обрабатываются быстрее, чем вывод данных PHP. Этот прием называется профессиональной вставкой. Для больших объёмов данных различия во времени достаточно заметны. readfile
быстрее, чемfile_get_contents
,file_get_contents
быстрее, чемrequire
, аrequire
быстрее, чемinclude
для вывода статического контента из отдельного файла
По времени считывания пустого файла колебания от 0.001 дляreadfile
до 0.002 дляinclude
.require
быстрее, чемinclude
для интерпретируемых файлов
Замечание: при ветвлении алгоритма, когда есть возможность не использовать интерпретируемый файл, надо использоватьinclude
, т.к.require
подключает файл всегда.if (...) {...} else if (...) {}
быстрее, чемswitch
Время зависит от количества веток.if (...) {...} else if (...) {}
быстрее, чемif (...) {...}; if (...) {};
Время зависит от количества веток и условий. Необходимо использоватьelse if
везде, где это возможно, так как это самая быстрая «условная» конструкция.- Наиболее часто встречающиеся условия конструкции
if (...) {...} else if (...) {}
надо помещать в начале ветвления
Интерпритатор просматривает конструкцию сверху вниз, пока не найдет выполнение условия. Если интерпретатор находит выполнение условия, то остальныю часть конструкции он не просматривает. x = sizeOf($array); for($i = 0; $i < x; ++$i) {...}
быстрее, чемfor($i = 0; $i < sizeOf($array); ++$i) {...}
Это вызвано тем, что во втором случае операция sizeOf будет выполнятся при каждой итерации. Время разницы выполнения зависит от числа элементов массива.x = sizeOf($array); for($i = 0; $i < x; ++$i) {...}
быстрее, чемforeach($arr as $value) {...}
для не ассоциативных массивов
Разница во времени значительна и увеличивается при увеличении массива.preg _replace
быстрее, чемereg_replace
,str_replace
быстрее, чемpreg_replace
, ноstrtr
быстрее, чемstr_replace
Разница во времени зависит от объёма данных и может достигать нескольких тысячных секунд.- Функции работы со строками быстрее, чем регулярные выражения
Это правило является следствием предыдущего. - Удаляйте уже ненужные переменные-массивы для освобождения памяти.
- Старайтесь не использовать подавление ошибок
@
Подавление ошибок производит ряд очень медленных операций, а так как частота повтора может быть очень большой, потери скорости могут быть значительными. if (isset($str{5})) {...}
быстрее, чемif (strlen($str)>4){...}
Это вызвано тем, что вместо функции для работы со строкамиstrlen
используется стандартная операция проверкиisset
.0.5
быстрее, чем1/2
Причина в том, что во втором случае выполняется операция деления.return
быстрее, чемglobal
при возвращении значения переменной из функции
Это вызвано тем, что во втором случае создаётся глобальная переменная.$row['id']
быстрее, чем$row[id]
Первый вариант быстрее в 7 раз.$_SERVER[’REQUEST_TIME’]
быстрее, чемtime()
для определения времени запуска скрипта
Причина в том, что в первом случае нет использования функции.if ($var===null) {...}
быстрее, чемif (is_null($var)) {...}
Причина в том, что в первом случае нет использования функции.++i
быстрее, чемi++
,--i
быстрее, чемi--
Это вызвано особенностями ядра PHP. Разница по времени менее 0.000001, но если у Вас данные процедуры повторяются тысячи раз, то присмотритесь к данной оптимизации.- Инкремент инициализированной переменой
i=0; ++i;
быстрее, чем не инициализированной++i
Разница по времени около 0.000001 секунды, но из-за возможной частоты повтора следует помнить данный факт. - Использование «отработавших» переменных быстрее, чем объявление новых
Или перефразирую иначе – Не создавайте лишних переменных. - Работа с локальными переменными быстрее, чем с глобальными, примерно, в 2 раза
Хоть и разница во времени менее 0.000001 секунды, но из-за высокой частоты повторения следует стараться работать с локальными переменными. - Обращение к переменной напрямую быстрее, чем вызов функции, внутри которой определяется эта переменная в несколько раз
На вызов функции тратиться примерно в три раза больше времени, чем на вызов переменной.
Бесполезная оптимизация
Ряд методов оптимизации на практике не оказывают большого влияния на скорость выполнения скриптов (выигрыш времени менее 0.000001 секунды). Несмотря на это, такая оптимизация зачастую становиться предметом споров. Я привел данные «бесполезные» факты для того, чтобы вы в последующим не уделяли им особого внимания при написании кода.
echo
быстрее, чемprint
include('абсолютный путь')
быстрее, чемinclude('относительный путь')
sizeOf
быстрее, чемcount
foreach ($arr as $key => $value) {...}
быстрее, чемreset ($arr); while (list($key, $value) = each ($arr)) {...}
для ассоциативных массивов- Не комментированный код быстрее, чем комментированный, так как уходит дополнительное время на чтение файла
Весьма глупо уменьшать объём комментариев ради оптимизации, надо просто в рабочих («боевых») скриптах проводить минимизацию. - Переменные с короткими названиями быстрее, чем переменные с длинными названиями
Это вызвано сокращением объёмов обрабатываемого кода. Аналогично предыдущему пункту, надо просто в рабочих («боевых») скриптах проводить минимизацию. - Разметка кода с использованием табуляции быстрее, чем с использованием пробелов
Аналогично предыдущему пункту.
Напоследок, хочу раз напомнить, что приведённые мною советы и факты не абсолютны и значимость их применения зависит от конкретной ситуации. Необходимо помнить, что оптимизация скриптов лишь малая часть от всей процедуры оптимизации и зачастую возможно спокойно жить без вышеописанных советов.