B.Loop

Вы наверняка знакомы с циклом в бенчмарках (for range b.N):

var sink int

func BenchmarkSlicesMax(b *testing.B) {
    // Setup the benchmark.
    s := randomSlice(10_000)
    b.ResetTimer()

    // Run the benchmark.
    for range b.N {
        sink = slices.Max(s)
    }
}

Go сам управляет бенчмарком, определяет разумное значение b.N, и пишет результаты. Это удобно.

Но есть и нюансы:

  • Функция бенчмарка выполняется несколько раз, поэтому сетап тоже выполняется несколько раз (и ничего с этим не поделаешь).
  • Чтобы сетап не повлиял на результат, приходится вызывать b.ResetTimer().
  • Чтобы компилятор не заоптимизировал тестируемый код, приходится использовать sink.

Go 1.24 предлагает кое-что получше — testing.B.Loop:

func BenchmarkSlicesMax(b *testing.B) {
    // Setup the benchmark.
    s := randomSlice(10_000)

    // Run the benchmark.
    for b.Loop() {
        slices.Max(s)
    }
}

b.Loop решает все проблемы b.N:

  • Функция бенчмарка выполняется один раз, поэтому и сетап тоже выполняется только однажды.
  • Все, что находится вне b.Loop, не влияет на результат, поэтому b.ResetTimer() не нужен.
  • Компилятор никогда не оптимизирует вызовы функций внутри b.Loop.

В общем, с выходом Go 1.24 больше нет причин использовать for range b.N. Переходим на b.Loop!

★ Подписывайтесь на новые заметки.