Быстрое профилирование
Как по-быстрому проверить нет ли в PHP-коде очевидных просадок по производительности?
Нам понадобится: установленный xdebug (версия 3, для версии 2 будут другие названия параметров в php.ini), браузер или phpStorm, опционально docker.
Включаем режим профилирования в xdebug. Для этого в php.ini добавляем это:
# или debug,profile, если хотим комбинированный режим
xdebug.mode=profile
и это:
# куда писать файлы с результатами
xdebug.output_dir=/var/profiling
# не архивировать файлы
xdebug.use_compression=false
# шаблон имен файлов
xdebug.profiler_output_name=cachegrind.out.%H.%R
, где /var/profiling
- абсолютный путь к папке, куда профайлер будет складывать результаты работы. Если проект в докере, эта папка должна быть доступна с хоста. Для этого удобно поместить её где-то внутри папки приложения, т.к. папка приложения в процессе разработки уже, как правило, маппится в докер-контейнер. А для того чтоб созданные файлы не добавлялись в git, можно поместить эту папку внутрь какой-то уже добавленной в .gitignore, например, var
или runtime
. Путь должен быть абсолютным внутри контейнера, то есть если у нас папка проекта /home/user/myapp
маппится на /var/www
в докер-контейнере, то надо указать /var/www/var/profiling
.
Создаем папку и даем права на запись:
mkdir /home/user/myapp/var/profiling \
&& chmod -R 777 /home/user/myapp/var/profiling
Перезапускаем PHP-FPM. Если он докеризирован, это проще всего сделать так:
docker exec -u root my_phpfpm_container_name bash -c "kill -USR2 1"
После этого запускаем приложение и убеждаемся что в указанной папке появился файл вида cachegrind.out...
. Прочитать этот файл можно разными средствами, проще всего - в PhpStorm или с помощью webgrind.
PhpStorm
Tools → Analyze Xdebug Profiler Snapshot… → выбираем нужный файл из результатов профайлинга и изучаем.
Webgrind
Удобный докеризированный web-интерфейс: https://github.com/jokkedk/webgrind. Запускаем так:
docker run --rm -v /home/user/myapp/var/profiling:/tmp -p 8089:80 jokkedk/webgrind:latest
, где /home/user/myapp/var/profiling
и 8089
- путь к папке с результатами профайлинга на хосте и порт, на котром поднимать web-интерфейс.
Переходим на http://localhost:8089
, выбираем нужный файл и изучаем. По умолчанию webgrind сканирует файлы, чьи имена начинаются на cachegrind.out
и я не нашел способа это изменить, поэтому нужно чтоб значение xdebug.profiler_output_name
в php.ini имело такой префикс (по умолчанию оно его имеет).
Как анализировать результаты
Результат профайлинга представляет собой список вызовов функций и методов. Для каждого из вызовов показывается:
- сколько раз он был выполнен (calls),
- сколько времени занял, включая вложенные вызовы (inclusive time или просто time)
- сколько времени занял, не включая вложенные вызовы (exclusive time или own time),
- методы и функции, из которых делался этот вызов (callers),
- методы и функции, вызванные из этого вызова (вложенные, callee).
Для поверхностного анализа узких мест достсточно отсортировать вызовы по убыванию по exclusive time и мы увидим самые “тяжелые” методы, с информацией о том, откуда и сколько раз они были вызваны. Также полезно отсортировать вызовы по убыванию по количеству (calls) - так можно заметить места в коде, которые можно оптимизировать путем кеширования результатов предыдущих вызовов.
Весь процесс занимает несколько минут и позволяет убедиться что написанный код не содержит каких-то очевидных завтыков, например “тяжелых” вызовов в цикле, лишних запросов к базе данных и т.д.
Рекомендую выполнять такую проверку для всего относительно сложного нового кода перед деплоем.