Привет, Хабр. Хочу рассказать историю, как я некоторое время назад занялся профилированием 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.

А каким профилировщиком пользуетесь вы? Поделитесь опытом :-)

Комментарии (3)


  1. FanatPHP
    11.07.2025 16:05

    Интересно, вспомнит ли кто-нибудь пинбу... :)


    1. Vitaly48
      11.07.2025 16:05

      Нормальный инструмент, но всё же пинба не про профилирование


  1. des1roer
    11.07.2025 16:05

    не верится что нагрузки не дает. хочется замеров