Профилирование производительности
Профилирование — это получение измерений о том, где программа тратит CPU, память, время ожидания и системные ресурсы. Его цель не «найти самую дорогую функцию» в вакууме, а проверить гипотезу на репрезентативной нагрузке: какой пользовательский SLI нарушен, какой ресурс насыщен, какой участок является причиной и улучшает ли изменение целевую метрику без регрессии.
Зачем это на интервью
Сильный кандидат не предлагает «оптимизировать всё» и не верит одному flame graph. Он формулирует workload, снимает baseline, выбирает профиль по симптомам, интерпретирует sampling корректно и показывает результат повторным измерением. Для Go это означает различать CPU, heap/allocs, goroutine, block, mutex и execution trace; для Linux — дополнять их perf, cgroup и системными метриками.
Минимум для E4
- До оптимизации определить SLI, workload, baseline, версию бинарника, окружение и критерий успеха.
- Уметь собрать CPU profile (
pprof), heap/allocs profile и benchmark с-benchmemдля Go-кода. - Отличать
flatотcum: flat показывает время в самой функции, cumulative — время в ней и вызовах ниже. - Выбирать профиль по вопросу: CPU — где исполнялись инструкции, allocs — где создавались объекты, block/mutex — где ожидали, trace — как планировались goroutine.
- Делать одно узкое изменение, сохранять тесты и повторять тот же замер; не считать визуальную смену flame graph доказательством.
Углубление для E5/Senior
Сначала эксперимент, затем инструмент
Нагрузочный сценарий фиксирует входные данные, arrival/concurrency model, длительность, warm-up, CPU quota, настройки runtime и cache state. Иначе сравнение профилей не причинно: один прогон может отличаться сетевым состоянием, другой — частотой CPU или количеством ошибок. Для latency-sensitive сервисов одновременно записывают success throughput, errors, p50/p95/p99 и saturation из распределения latency.
Sampling CPU profile статистичен: он показывает, где поток исполнялся в момент выборок, и может не уловить редкий stall. CPU profile не доказывает, что функция вызвала p99; она могла эффективно занимать CPU в быстрых запросах. Для хвоста нужны trace, exemplars, профили под перегрузкой и корреляция с зависимостями. С другой стороны, wall-clock profile без понимания блокировок может обвинить функцию, которая лишь стоит выше ожидающего вызова.
Go-профили по назначению
go test ./internal/handler -run '^$' -bench '^BenchmarkDecode$' \
-benchmem -count=5 -cpuprofile=cpu.out -memprofile=mem.out
go tool pprof -http=:0 cpu.out
go tool pprof -sample_index=alloc_space mem.outHeap profile отвечает на два разных вопроса. inuse_space показывает удерживаемую память на момент снимка; alloc_space — места, которые выделили много байтов за весь интервал. Первое ищет retention/leak, второе — GC pressure. Block и mutex profiles требуют настройки частоты sampling и могут иметь overhead; включайте их на коротком контролируемом интервале. go tool trace показывает scheduling, network/syscall blocking и GC, но файл trace может быть большим и сам влиять на нагрузку.
Профили с production endpoint должны быть защищены: pprof не публикуют в интернет, доступ ограничивают сетью и аутентификацией, интервалы и overhead согласуют с владельцем сервиса. Сырые profile/trace могут содержать имена функций, пути и данные контекста — храните их как чувствительные operational artifacts по политике команды.
Системная граница и differential profiling
Если CPU профайл приложения «чистый», это не означает отсутствия bottleneck. Процесс может ждать сеть, filesystem, scheduler, cgroup throttling или remote dependency. Смотрите runtime metrics, /proc, PSI, cgroup cpu.stat/memory.events, distributed trace и, когда безопасно, perf. perf record/perf report дают kernel+user view, но символы, права и overhead влияют на качество результата.
Полезный метод — differential profiling: собрать baseline и candidate при том же workload, затем сравнить изменившиеся hotspots, allocation sites и SLI. Но уменьшение функции в профиле не всегда выигрыш: перенос работы в другую функцию, падение throughput или рост ошибок создают ложную победу. Результат должен включать guardrails: correctness tests, CPU/memory budget и rollback condition.
Ключевые понятия
| Понятие | Суть | Не путать с |
|---|---|---|
| Baseline | Воспроизводимое измерение до изменения | Личным ощущением «медленно» |
| CPU profile | Сэмплы исполнявшегося CPU кода | Полным wall-clock путём запроса |
| Heap in-use | Удерживаемая память в снимке | Всеми аллокациями за период |
| Allocation profile | Места выделений за интервал | Утечкой автоматически |
| Block/mutex profile | Наблюдаемое ожидание блокировок/lock | Причиной всех context switches |
| Flame graph | Визуализация стека и веса | Доказательством причинности |
Типовые вопросы
- Почему top CPU function не всегда надо оптимизировать?
- Она может быть ожидаемой полезной работой, не связанной с нарушенным SLI, или уже эффективной. Нужны доля затрат, альтернатива, стоимость изменения и измеренный выигрыш end-to-end.
- Чем
alloc_spaceотличается отinuse_space?alloc_spaceпоказывает общий поток выделений за период и помогает искать GC pressure;inuse_spaceпоказывает то, что удерживается в момент профиля, и полезен для retention.
- Когда брать mutex profile?
- Когда есть гипотеза о lock contention: рост waiting, ухудшение p99, много конкурирующих goroutine. Включить на ограниченный интервал и сопоставить с workload, а не постоянно без причины.
- Почему benchmark может врать?
- Компилятор способен исключить неиспользуемую работу; данные могут быть нереалистичны, cache тёплым, а concurrency иной. Результат надо потреблять, фиксировать вход и сверять с интеграционным SLI.
- Что делать, если pprof не показывает CPU hotspot, а p99 плохой?
- Проверить очереди, blocking, GC, scheduler, I/O, cgroup throttling и downstream traces. P99 может быть ожиданием, не CPU-вычислением.
- Как безопасно снять профиль в production?
- Ограничить доступ к endpoint, выбрать короткий согласованный интервал и реплику/канарейку, оценить overhead, не записывать секреты и заранее иметь план удаления артефакта.
Практика
- Постройте воспроизводимый baseline. Для небольшого Go handler подготовьте benchmark и интеграционный load test, который формирует реалистичный payload.
- Критерии готовности: указаны commit/binary, Go version, CPU quota, duration, warm-up,
-count=5, p50/p95/p99, success RPS и error rate; benchmark возвращает результат работы, чтобы его не выкинул оптимизатор.
- Критерии готовности: указаны commit/binary, Go version, CPU quota, duration, warm-up,
- Проведите одну оптимизацию по профилю. Соберите CPU и alloc profiles, выберите один подтверждённый allocation/hotspot, внесите минимальный change и повторите весь сценарий.
- Критерии готовности: сохранены исходный и итоговый профили, объяснены
flat/cumилиalloc_space/inuse_space, тесты проходят, а выигрыш и отсутствие регрессии показаны числами.
- Критерии готовности: сохранены исходный и итоговый профили, объяснены
- Разберите не-CPU деградацию. Добавьте искусственную задержку mutex либо downstream I/O и сравните CPU, block/mutex profile,
go tool traceи HTTP histogram.- Критерии готовности: сделана корректная гипотеза о типе ожидания, CPU profile не объявлен причиной, есть trace/метрика очереди и предложен bounded fix с критерием rollback.
Частые ошибки и ловушки
- Начинать с оптимизации до baseline и затем выбирать удобный график как доказательство.
- Путать cumulative время вызывающей функции с её собственным CPU.
- Искать memory leak по
alloc_spaceили GC pressure только поinuse_space. - Снимать profile под другой нагрузкой после изменения и сравнивать картинки.
- Включать тяжёлые профили/trace бесконечно в production или оставлять pprof публично доступным.
- Делать вывод о коде без проверки scheduler, cgroup и зависимостей.
Связанные темы
Модуль Foundations · Go · Наблюдаемость · Скорость аллокаций и GC · Ложное разделение · Throughput и tail latency
Источники
- Go documentation:
runtime/pprof,net/http/pprof,go testflags и Execution Tracer. - Go blog: Profiling Go Programs и Diagnostics.
- Linux
perfdocumentation:perf-record(1),perf-report(1)иperf-stat(1). - Brendan Gregg, Systems Performance, главы о методах наблюдаемости и профилировании.
- Google SRE Book, Monitoring Distributed Systems.