Бенчмарки Go и benchmem
Benchmark измеряет малую операцию в контролируемом процессе. Он полезен для сравнения реализаций при фиксированном workload, но не доказывает улучшение latency сервиса без интеграционного измерения.
Зачем это на интервью
Интервьюер ищет дисциплину «гипотеза → baseline → одно изменение → повторение», а не число ns/op без контекста.
Минимум для E4
- Писать
func BenchmarkX(b *testing.B)в_test.go. - Выполнять подготовку вне цикла и использовать результат, чтобы компилятор не выкинул работу.
- Запускать
go test -bench '^BenchmarkX$' -benchmem -count=5. - Читать
ns/op,B/op,allocs/opвместе с входом и correctness test.
var sink int
func BenchmarkSum(b *testing.B) {
data := make([]int, 1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
sink = sum(data)
}
}Углубление для E5/Senior
b.N подбирается раннером; не задавайте число вручную. Для разных размеров входа применяют sub-benchmarks (b.Run) и b.SetBytes для throughput. b.ReportAllocs() включает allocation metrics программно, -benchmem — для прогона. b.StopTimer/StartTimer допустимы для исключения неизбежимой setup-работы, но не для скрытия реальных затрат операции.
Сравнивайте несколько прогонов на сходном железе и без параллельной нагрузки; для статистического сравнения используют benchstat на сохранённых текстовых результатах. Изменение может улучшить микротест, но увеличить contention, память или сложность API. Поэтому сохраняют baseline, запускают unit tests и при необходимости нагрузочный тест.
Ключевые понятия
| Метрика | Что показывает |
|---|---|
ns/op | средняя стоимость одной операции |
B/op | выделенные байты на операцию |
allocs/op | число выделений на операцию |
b.N | автоматически выбранное число итераций |
Типовые вопросы
- Зачем global sink? — Чтобы результат наблюдался и вычисление не было оптимизировано как мёртвое.
- Что измеряет
-benchmem? — Выделения и байты на операцию, а не retained heap после всего процесса. - Почему одна цифра ненадёжна? — Шум CPU, cache, GC и scheduler требует повторов и сравнимого окружения.
- Когда нужен
RunParallel? — Когда контракт операции конкурентный; он измеряет другой workload и не заменяет последовательный benchmark. - Почему уменьшение allocs не всегда победа? — Возможны рост CPU, удержания памяти, lock contention или ухудшение читаемости.
Практика
- Сравните две реализации сериализации на трёх размерах input. Готово: есть sub-benchmarks,
-benchmem, пять прогонов и корректность результата. - Уберите одну подтверждённую аллокацию. Готово: сохранены до/после, объяснён trade-off,
go test ./...проходит.
Частые ошибки и ловушки
- Генерировать input внутри измеряемого цикла без намерения измерять генерацию.
- Бенчмаркать I/O без контролируемого сервера и называть результат CPU-скоростью.
- Делать вывод из одной записи или сравнивать разные версии Go.
Связанные темы
Инструменты Go · Производительность и память · pprof