Привет, Хабр. Хочу рассказать историю, как я некоторое время назад занялся профилированием PHP — и куда это меня привело.
Меня зовут Олег, я больше 15 лет пишу код, проектирую системы и руковожу командами. Ещё выступаю на профильных конференциях, таких как Highload++, PHPRussia. Руковожу программным комитетом PHP-секции в рамках конференции “Стачка”.
Тема Observability мне интересна: я постоянно стараюсь тюнить это в проекте, над которым работаю. На текущем месте я развивал всё шаг за шагом — от алертов до трейсов.
Обычно, когда речь заходит о профилировании PHP-приложений, вспоминают проверенные временем инструменты: XHProf, PHP Spy, Xdebug — если говорить про open source. Blackfire, Tideways, NewRelic, Sentry или Datadog — если из коммерческих решений. Как вы заметили, в этом списке нет никакого Excimer. Откуда он взялся и зачем его использовать в современном PHP?
На самом деле, про него мало кто вообще слышал. Я и сам узнал случайно, когда затаскивал трейсинг и профайлинг в наш Sentry, с которым он работает из коробки через SDK. Стало интересно, почему выбор пал именно на такой малоизвестный инструмент — и что ещё с этим можно сделать. Да и вообще: как обстоят дела с профайлингом PHP в 2025 году?
Итак, нам нужно решение, которое:
для постоянного сбора данных с прода,
имеет возможность отправлять трейсы и профайлы,
может связать их через общий trace_id,
чтобы было удобно просматривать в едином интерфейсе,
и в идеале — заменить хранилище и визуализатор без изменений в коде.
Про последнее сразу вспоминается OpenTelemetry — стандарт для observability. По сути, реализуем общение между нашим бэкендом и инструментом по протоколу.
Коммерческие инструменты: Datadog, Blackfire, Tideways
Прекрасные инструменты. Подходят для продакшена, не нагружают CPU при сборе профайлов, не требуют тонкой настройки. Совместимы с последними версиями PHP.
Конечно, это коммерческие решения, которые стали недоступны для большинства компаний в РФ. Зато предоставляют большой инструментарий для сбора трейсов и профайлов.
Ещё одна задача — возможный переезд с одного инструмента на другой. Поскольку это проприетарные решения, всегда есть риск вендор-лока.
XHProf
До сих пор пользуется популярностью в сообществе. В продакшене стоит использовать с осторожностью: может создавать нагрузку при высоком трафике. Потеря производительности — от 20–30% до 200%.
Кроме того, последний релиз был в 2020 году, а последний коммит — два года назад. Репозиторий не выглядит живым. Но он всё ещё работает!
Формат OpenTelemetry не поддерживается, с трейсами связать тоже не выйдет.
PHP Spy
Работает отдельно от вашего PHP-приложения и собирает метрики с php-fpm. Учитывая новости, что The PHP Foundation взяла под своё крыло альтернативный рантайм FrankenPHP, я думаю, что было бы классно иметь более универсальный инструмент, не зависящий от рантайма.
Ещё одна проблема — связать профайл с трейсом. Передать trace_id наружу? Можно, но задача нетривиальная.
Тем не менее, PHP Spy обладает рядом плюсов:
поддерживает OpenTelemetry,
достаточно производительный,
нет необходимости вмешиваться в код.
К сожалению, его поддержка сейчас оставляет желать лучшего. Но есть живые форки.
Xdebug
Для постоянного профайла в продакшена всерьёз даже не рассматривается :) xDebug 3 версии можно включать через куку без заметных просадок по производительности.
Для локального профайлинга и дебаггинга — незаменимый инструмент.
Excimer?
Это профайлер, который использует и развивает Wikimedia. Написан на C, ставится как расширение PHP и работает в контексте приложения. Оказывает незначительную нагрузку на CPU и отлично подходит для прода.
Его ключевая особенность: использование POSIX таймеров (timer signals) для выборочного сэмплирования стека вызовов.
Особенности Архитектуры
Excimer использует setitimer(ITIMER_PROF) или timer_create + SIGPROF.
Таймеры запускаются в отдельном потоке ядра и прерывают исполнение PHP с заданным интервалом (--period=0.01 — 100 сэмплов/сек).
На каждый сигнал выполняется обработчик, в котором собирается стек вызовов.
И поскольку таймеры POSIX работают на уровне ядра, они практически не замедляют основной поток исполнения.
В момент сигнала SIGPROF Excimer вызывает zend_fetch_debug_backtrace(), чтобы получить текущий стек. Полученные сэмплы складываются в кольцевой буфер в памяти.
Но: не может отправлять данные по стандарту OpenTelemetry. По сути, работает только с Sentry и WikimediaDebug (уверен, про него вы тоже ничего не слышали), причём только в локальном режиме.
Я решил, что было бы классно научить Excimer работать с инструментами, поддерживающими OpenTelemetry — и в целом с чем-то, кроме WikimediaDebug. Так родился мой первый open source проект — excimetry.
Что реализовано на данный момент:
Возможность отправлять профайлы в Pyroscope
Возможность отправки в формате OpenTelemetry (protobuf)
Экспорт профайлов в формате Speedscope
Выгрузка просто в файл (в формате любого из exporters)
Поддержка профилирования CLI-команд
// Создадим профилировщик, можно передать необходимые конфиги
$profiler = new Excimetry\Excimetry();
// начинаем профилирование
$profiler->start();
// какой-то код
// ...
// останавливаем профилирование
$profiler->stop();
Далее нужно создать экспортёр и выбрать как будет выполняться отправка.
use Excimetry\Profiler\ExcimerProfiler;
use Excimetry\Exporter\CollapsedExporter;
use Excimetry\Backend\PyroscopeBackend;
$backend = new PyroscopeBackend(
serverUrl: 'http://localhost:4040',
appName: 'my-application',
labels: ['env' => 'production'],
exporter: new CollapsedExporter(),
);
// Добавим лейблы. Например, trace_id
$backend->addLabel('trace_id', 'some_trace_id');
$backend->addLabel('region', 'Saint-P');
// Отправим а Pyroscope
$backend->send($profiler->getLog());
// Или то же самое, но асинхронно
$backend->setAsync(true);
$backend->send($profiler->getLog());
Иногда требуется снять профайл с консольной команды или консольного скрипта. Это тоже возможно:
excimetry-profile \
--period=0.01 \
--mode=wall \
--format=speedscope \
--output=profiles \
path/to/script.php
Несколько месяцев уже у нас работает этот профилировщик продакшене, пробовали отправлять до 30% трафика, на производительности это не сказалось. Сейчас, конечно, сильно меньше, потому что Sentry не справляется с нагрузкой. В планах переехать на Grafana Stack как на более производительный и специализированный.
Так как для меня это первый опыт в open source буду рад обратной связи тут или в issue на github
Конечно, я не могу упустить возможность сказать вам, что много интересного об архитектуре и разработке публикую у себя в telegram канале - https://t.me/ask_for_oleg.
А каким профилировщиком пользуетесь вы? Поделитесь опытом :-)
FanatPHP
Интересно, вспомнит ли кто-нибудь пинбу... :)
Vitaly48
Нормальный инструмент, но всё же пинба не про профилирование