Бенчмарк измеряет скорость и аллокации функции. Он не заменяет нагрузочный тест: показывает стоимость конкретной операции, а не всей системы.
Каркас
func BenchmarkBuildResponse(b *testing.B) {
for b.Loop() {
_ = buildResponse(input)
}
}
b.N выбирает рантайм так, чтобы замер был достаточно долгим. Внутри цикла не должно быть настройки на каждую итерацию: она попадёт в результат. Подготовку выносят до b.ResetTimer().
go test -bench . -benchmem показывает время, байты и аллокации на операцию. -benchtime=2s лишь задаёт длительность одного замера, но не доказывает устойчивость: для неё запускают серию через -count=10 и сравнивают распределения через benchstat.
Что проверять
Сравнивают не две функции «на глаз», а одну и ту же работу с одинаковыми данными:
- простой вариант и оптимизированный;
- малый, средний и большой вход;
- лучший, типичный и худший случай;
- параллельную и последовательную версии, если есть конкуренция.
Для параллельного бенчмарка используют b.RunParallel, помня, что он измеряет ещё и contention.
benchstat
benchstat сравнивает серии результатов:
go test -bench . -benchmem -count=10 > old.txt
go test -bench . -benchmem -count=10 > new.txt
benchstat old.txt new.txt
Он показывает разницу и доверительный интервал. Если интервал включает ноль, улучшение не доказано. Меньше шума не обязательно значит лучше.
Типовые ошибки
- вход создаётся внутри цикла и доминирует в замере;
- компилятор устраняет результат как неиспользуемый;
- сравниваются разные входы;
- делается вывод по одному запуску;
time.Sleepпринимается за стоимость функции.
Результат присваивают в глобальную переменную, чтобы оптимизатор не удалил вычисление.
Коротко
testing.Bизмеряет операцию, не всю систему.benchmemпоказывает аллокации на операцию.benchstatсравнивает серии и их устойчивость.- Подготовка и вход не должны попадать в измеряемую работу.