Производительность Go
Оптимизация без измерения — это угадывание. Go даёт встроенный профайлер прямо в runtime: он всегда доступен, работает на боевом сервисе и не требует особого флага сборки. Сначала вы снимаете профиль, потом читаете его — и только тогда знаете, куда уходит время. Главная ошибка здесь — судить о природе нагрузки по одной задержке: большая задержка бывает и у CPU-bound, и у I/O-bound сервиса, и без загрузки CPU и профиля это два неотличимых случая.
Когда узкое место найдено, у вас есть рычаги: направить компилятор на горячие пути боевым профилем (PGO), масштабировать сервис вширь или вверх. Но любая оптимизация рискует сломать поведение — поэтому вторая половина темы про тестирование. В Go тест — часть тулчейна, а не сторонняя библиотека: go test сам собирает тестовый бинарник, гоняет table-driven случаи, бенчмарки и fuzzing. А чтобы юнит-тест проверял вашу логику, а не доступность базы и сети, зависимость прячут за узкий interface и подставляют в тесте заглушку. Тема разбирает этот цикл — от снятия профиля до изолированного теста.
Карта темы
- Профилирование Go — как снять CPU, heap, goroutine, block и mutex профили через
pprofи разобрать ихgo tool pprof. - Анализ узких мест — как по загрузке CPU и профилю отличить CPU-bound сервис от I/O-bound.
- Profile-Guided Optimization — профиль
default.pgoв main-пакете направляет компилятор на инлайнинг и девиртуализацию горячих путей. - Стратегии масштабирования — горизонтальное добавляет экземпляры за балансировщиком, вертикальное даёт одному экземпляру больше ресурсов.
- Тестирование в Go —
go testбез фреймворка: unit и table-driven тесты, бенчмарки, examples и fuzzing, плюс флаги-race/-cover. - Мокирование зависимостей — зависимость от узкого интерфейса и подстановка заглушки в тесте изолируют юнит от реального I/O.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
| Думать, что профилирование требует особого флага сборки | Достаточно импорта net/http/pprof или вызова runtime/pprof |
| Считать, что CPU-профиль — мгновенный срез | Это окно семплирования; программа должна поработать под нагрузкой во время сбора |
Выставлять порт pprof наружу | /debug/pprof/* отдаёт внутренности сервиса — порт только для внутренней сети |
| Судить о природе нагрузки только по задержке | Без загрузки CPU и профиля не отличить CPU-bound от I/O-bound |
| Забыть про троттлинг cgroup при диагнозе | CPU-лимит cgroup маскирует CPU-bound под I/O-bound — низкая загрузка при росте задержки |
| Считать, что заблокированная горутина жжёт CPU | Горутина в net-ожидании припаркована и нагрузки не создаёт |
Класть профиль PGO не как default.pgo или не в main-пакет | go build не подхватит профиль — оптимизация молча не применится |
| Путать горизонтальное и вертикальное масштабирование | Горизонтальное добавляет экземпляры, вертикальное — ресурсы одному |
| Считать, что вверх можно расти бесконечно | Вертикальное упирается в самую мощную машину и обычно требует перезапуска pod |
| Задавать число итераций бенчмарка вручную | b.N подбирает тул — ручное число ломает измерение |
Тянуть в interface все методы реального клиента | Чем шире контракт, тем больше кода в фейке и тем выше связанность — берите только нужное |
| Лезть к реальной зависимости через глобальную переменную | Глобальное состояние течёт между тестами и ломает параллельный прогон |
Значение для собеседований
Вопросы о производительности на Go-интервью проверяют не знание команд, а методику: умеете ли вы найти узкое место измерением, а не угадыванием, и не сломать поведение при оптимизации. Интервьюер хочет видеть цикл «снять профиль → прочитать → диагноз → проверить тестом».
Что обычно проверяют:
- Как профилировать Go-сервис в production через
pprof— без особого флага сборки. - Как по загрузке CPU и профилю отличить CPU-bound сервис от I/O-bound.
- Что такое PGO и куда положить профиль, чтобы компилятор его подхватил.
- Чем горизонтальное масштабирование отличается от вертикального и что каждое требует.
- Как устроено тестирование в Go — table-driven тесты, бенчмарки, флаги
-race/-cover. - Как изолировать юнит от реального I/O — узкий
interfaceи заглушка вместо глобального состояния.
Типичный неверный ответ: «сервис тормозит — значит, он I/O-bound, добавим ядер не поможет». Это запускает разбор того, что без загрузки CPU и профиля диагноз поставить нельзя, что троттлинг cgroup маскирует CPU-bound под I/O-bound, и что направление лечения определяет именно измерение.