Performance Engineering — methodology in action. Three scenarios: (1) CPU flame graph reveals 60% CPU in jackson.ObjectMapper init — fix with static singleton beats horizontal scale by 4×; (2) Continuous profiling (Pyroscope) catches a regression in v2.4 (gzip on tiny payloads) within 1h via version diff; (3) Load test with k6 surfaces tail latency at p99 — mean lies (45ms), p99 is 2.8s due to autovacuum stop-the-world; tuned + hedged requests bring p99 to 280ms. Topology: production service (lb/api/cache/db) instrumented with /debug/pprof, scraped by Pyroscope into a flame graph UI. SRE tooling: k6 load gen, Prometheus RED metrics, Grafana p99 dashboard, Tempo traces. On-call engineer drives flame graph inspection, SLO checks, and load runs. Two ADRs: optimize hot path before scaling horizontally; continuous profiling in prod over ad-hoc local profiling.
Performance — это продукт, а не «то, что мы дотюним к релизу». Цифры жёсткие: +100ms latency у Amazon = −1% sales; +500ms у Google = −20% трафика. Медленная-но-корректная система всегда проигрывает быстрой-и-корректной в B2C и почти всегда в B2B. Performance engineering — это методология, а не «попрофилил и нашёл узкое место»: измерь baseline → найди самый большой bottleneck → почини → провалидируй → повтори. Без методологии оптимизация превращается в полировку микросекунд там, где они не нужны, пока 1-секундная функция рядом остаётся нетронутой.
«Сначала измеряй, потом оптимизируй.» Donald Knuth: premature optimization is the root of all evil — но полная цитата заканчивается «yet we should not pass up our opportunities in that critical 3%». Performance engineering — это работа по нахождению того самого 3% и игнорированию остальных 97%.
Три инструмента дают разный ответ на разный вопрос:
В сумме они образуют drill-down: алерт по RED → trace показывает виновный span → profile показывает виновную функцию → лог даёт контекст запроса. Brendan Gregg в Systems Performance (2nd ed., 2020) формализует это как USE-method для ресурсов (Utilization/Saturation/Errors) и workload characterization для нагрузки.
Минимальный production-стенд:
lb → api (3 реплики, 5K RPS) → cache (Redis) + db (PostgreSQL). Типичная веб-архитектура, на которой видны все классические bottleneck-сценарии.pprof endpoint, Pyroscope (continuous profiler, scrape каждые 10s, ~1% CPU overhead), Flame graph UI. Пара pprof+Pyroscope — современный де-факто стандарт для continuous profiling в проде.k6 (synthetic load), Prometheus (RED-метрики), Grafana (p99-дашборд), Tempo (distributed traces).Рёбра — это физические соединения, а не направление данных. Например, edge api → pprof существует один раз: ответ на scrape от Pyroscope летит обратно по тому же ребру (reverse animation), отдельного ребра pprof → pyroscope нет.
CPU flame graph → fix. Классический drill-down: алерт «p99 800ms (SLO 200ms)» → Grafana показывает линейный рост за 2 недели → engineer открывает Pyroscope → flame graph: 60% CPU в jackson.ObjectMapper.writeValueAsString → root cause: на каждый запрос пересоздаётся ObjectMapper → fix: вынести в static final синглтон → валидация: p99 = 220ms. 4× ускорение от 10 строк кода. Это ровно то, ради чего нужен профайлер: интуиция сказала бы «надо больше нод», профайлер показал реальную причину.
Continuous profiling diff. Pyroscope scrape-ит /debug/pprof каждые 10s, теги по service.version. После деплоя v2.4 авто-алерт: «новая функция compress.gzipBody = +25% CPU». Diff между v2.3 и v2.4 показывает один новый стек. Root cause: PR #4821 включил gzip на каждый ответ, включая JSON < 1KB (где CPU-cost компрессии больше, чем сэкономленный байт). Fix: gzip только > 8KB. Без continuous profiling эта регрессия жила бы недели, пока кто-то случайно не запустил локальный профайлер.
Load test → tail at p99. k6 ramp 100→5000 RPS. На первый взгляд всё ок: mean = 45ms, CPU 60%. Но Grafana p99 = 2.8s, p99.9 = 8.1s — это и есть Tail at Scale (Dean & Barroso, CACM 2013). Trace показывает: 95% хвоста — в db-span. Root cause: PostgreSQL autovacuum блокирует hot table на 2 секунды. Mean соврал — 1% пользователей видит 2.8s. Два fix: (A) тюнить autovacuum_vacuum_cost_limit, чтобы размазать работу; (B) hedged requests (Dean) — переотправлять запрос, если ответа нет к p95. Итог: p99 = 280ms, mean не сдвинулся.
Контекст. Когда p99 растёт, есть два пути: (a) добавить реплик / взять instance побольше — быстро (1 день), но линейно умножает infra-cost навсегда; (b) спрофилировать и оптимизировать hot-функцию — медленно (1–2 недели), но компаундится: выигрыш получает каждый будущий запрос.
Решение. Сначала всегда профилируем, если у одной ноды есть < 10× headroom. Оптимизируем top-3 функции на flame graph, если хоть одна > 15% CPU. Горизонтальный scale включаем только когда (1) flame flat (никакая функция > 5%), либо (2) задача честно embarrassingly parallel и stateless. Для stateful (DB, брокеры) horizontal scale — last resort: шарды добавляют ops-сложность на годы вперёд.
Контекст. Локальное профилирование на синтетическом workload систематически прячет настоящий bottleneck — data scale, concurrency, cache state, mix запросов не совпадают с продом. Инженер неделю гоняется не за тем хот-спотом, потом видит prod-профиль и охреневает. Continuous profiling (Pyroscope/Parca через eBPF) стоит ~1% CPU и захватывает каждый коммит на реальном трафике.
Решение. Pyroscope (или Parca) на каждой prod-ноде с day 1. Профили тегируются по версии сервиса, чтобы можно было сделать diff v2.4 vs v2.3. Авто-алерт на регрессии > 10% в любой top-5 функции. Локальный профайл разрешён только для прототипирования fix-а, никогда как источник правды о том, «что медленно».
Контекст. Команда годами репортила mean latency, потому что «он стабильный и красивый на графиках». Tail при этом был катастрофой — 1% юзеров видел 2-секундные страницы.
Решение. SLO формулируется только в терминах p99 и p99.9. Mean можно показывать для отладки, но не как KPI. Hedged requests / tied requests рассматриваются по умолчанию для любых fan-out > 10 (Dean & Barroso: при fan-out на 100 серверов с p99=1s, 63% запросов увидят 1s где-нибудь).
EXPLAIN — missing index это 10–1000× win на одной команде. Самая дешёвая оптимизация в мире.Performance engineering как дисциплина имеет цену: профайлеры, observability, нагрузочные тесты, время инженеров. Бывают ситуации, когда это не первый приоритет:
И отдельно: не оптимизируй то, что юзер не чувствует. Если функция выполняется раз в сутки в background — её 5s vs 50ms никому не интересен. Hot path первичен.