Профилирование PHP7 кода с использованием xhprof
Со временем любой PHP-программист сталкивается с проблемой низкой производительности своего приложения. Это может быть медленная загрузка конкретной страницы или слишком долгий ответ от API. И порой достаточно сложно понять, в чем причина тормозов? Порой случаются более сложные ситуации: на боевом сервере api сильно тормозит, но на стенде, где происходит разработка, - все хорошо. И пойди разберись, что идет не так. Производить отладку на рабочем сервере - это крайняя степень безысходности, до которой, конечно, лучше не доводить.
Именно для таких ситуаций и были придуманы специальные инструменты, называемые профилировщиками приложений. В мире PHP эту роль выполняют xDebug, а также xhprof. xhprof является более легковесным, простым и гибким инструментом, поэтому его использование более предпочтительно. Что интересно, xhprof был разработан в facebook еще в 2009 году, однако до сих пор от них нет официальной поддержки php7 и больше не будет, поскольку facebook перешел на HHVM. Однако, благодаря обширному сообществу php-разработчиков, появился форк, поддерживающий php7, установка которого не вызывает каких-либо сложностей.
Установка
Для начала нужно, собственно, установить xhprof:
git clone https://github.com/longxinH/xhprof xhprof
cd xhprof/extension
phpize
./configure --with-php-config=/usr/bin/php-config
sudo make && sudo make install
mkdir /var/tmp/xhprof
Далее прописать настройки в php.ini:
[xhprof]
extension=xhprof.so
xhprof.output_dir="/var/tmp/xhprof"
Для папки /var/tmp/xhprof должен быть доступ на запись, т.к. туда будут сохраняться результаты профайлинга.
Можно перезагрузить PHP-FPM и проверить, установилось ли расширение. Банально, это можно сделать с помощью вывода функции phpinfo();

xhprof установлен, можно им пользоваться. В пакет xhprof входит очень удобный интерфейс для анализа отчетов профилирования. xhprof позволяет строить отчеты, как в текстовом так и графическом виде. В установочной папке xhprof находятся xhprof_html и xhprof_lib, которые нам понадобятся. Папка xhprof_html — предоставляет доступ к GUI. xhprof_lib — библиотека для отображения и анализа кода. Всю папку xhprof целесообразно перенести в /var/www/xhprof и настроить для нее виртуальный хост, например xhprof.loc. Пример для nginx:
server {
listen 80;
server_name xhprof.loc;
charset utf-8;
root /var/www/xhprof/xhprof_html;
index index.php;
location / {
try_files $uri $uri/ /index.php?q=$uri&$args;
}
location ~ .php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}Также необходимо не забыть обновить файл hosts. Теперь при введении в браузерe URL xhprof.loc мы будем попадать на веб-интерфейс профилировщика, где будут доступы сгенерированные им файлы.
Теперь можно приступить непосредственно к профилированию кода.
Для включения профилировщика используется функция xhprof_enable(), которая на вход принимает следующие флаги:
- XHPROF_FLAGS_CPU - для фиксирования статистики процессора;
- XHPROF_FLAGS_MEMORY — для памяти;
- XHPROF_FLAGS_NO_BUILTINS — для игнорирования встроенных функций.
Для отключения же профилировщика используется функция xhprof_disable().
Для удобства напишем два скрипта header.php и footer.php, выполняющие эти функции. header.php подключается в начало профилируемого скрипта, а footer.php — в конец. footer.php занимается также и сохранением данных профилирования.
header.php:
if (extension_loaded('xhprof')) {
include_once '/var/www/xhprof/xhprof_lib/utils/xhprof_lib.php';
include_once '/var/www/xhprof/xhprof_lib/utils/xhprof_runs.php';
xhprof_enable(XHPROF_FLAGS_CPU);
}footer.php:
if (extension_loaded('xhprof')) {
$profilerNamespace = 'ЗДЕСЬ_ИМЯ_ПРОФИЛИРУЕМОГО_СКРИПТА';
$xhprofData = xhprof_disable();
$xhprofRuns = new XHProfRuns_Default();
$runId = $xhprofRuns->save_run($xhprofData, $profilerNamespace);
}Использование
Подключив header.php и footer.php к профилируемому скриту, можно начинать: при выполнении профилируемого скрипта сгенерируется файл, который сохранится в директории /var/tmp/xhprof, содержащий информацию о работе скрипта. При открытии веб-интерфейса xhprof.loc, будет доступен этот сгенерированный файл:

При открытии файла профилирования появляется детальная информация о работе приложения, весь стек вызовов:

Что значат столбцы:
Calls — количество и процентное соотношение вызовов функции;
Incl. Wall Time — время выполнения функции с вложенными функциями;
Excl. Wall Time — время выполнения функции без вложенных функций;
Incl. CPU — процессорное время с вложенными функциями;
Excl. CPU — процессорное время без вложенных функций;
Incl. MemUse — потребление памяти с вложенными функциями;
Excl. MemUse — потребление памяти без вложенных функций;
Incl. PeakMemUse — максимальное потребление памяти с вложенными функциями;
Excl. PeakMemUse — максимальное потребление памяти без вложенных функций.
Если перейти по ссылке [View Full Callgraph], то отобразится прекрасное дерево вызовов с визуальной индикацией наиболее тормознутого кода. Если же этого не произошло, то скорее всего нужно установить библиотеку graphviz:
apt-get install graphviz
Пример построенного графика:

В моем случае самое узкое место — это взаимодействие с БД.
Использование xhprof на боевом сервере
Изначально xhprof разрабатывался именно с целью профилирования кода в бою, на production серверах. Другого бесплатного и эффективного инструмента профилирования php7-кода в бою по-просту нет, поэтому у xhprof нет конкурентов. Конкретно у меня есть опыт использования xhprof на production сервере, который обрабатывает миллион запросов в сутки. Там используется php7, и проблем пока не было обнаружено. Однако, xhprof не запускается для каждого запроса — генерировалось бы слишком много файлов профилирования. У меня профилировщик запускается только в том случае, если в запросе указан заголовок «XHPROF_ENABLE» и он установлен в true. Также можно использовать и другую стратегию, например, запускать профилировщик случайно с вероятностью, скажем, в 1/1000. Тогда тоже будет достаточно ясная картина.
Вывод
Даже несмотря на то, что xhprof официально не поддерживается для php7, он по-прежнему остается незаменимым инструментом для php-разработчика.
Comments