Бенчмарк измеряет скорость и аллокации функции. Он не заменяет нагрузочный тест: показывает стоимость конкретной операции, а не всей системы.

Каркас

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 сравнивает серии и их устойчивость.
  • Подготовка и вход не должны попадать в измеряемую работу.