testing.B.Loop în Go

Condiția buclei unui benchmark: returnează true cât timp mai trebuie repetat codul măsurat (Go 1.24).

Sintaxă
func (b *B) Loop() bool

Ce face testing.B.Loop

for b.Loop() { ... } este forma modernă a buclei de benchmark. Față de vechea buclă cu b.N, are trei avantaje: funcția benchmark rulează o singură dată (pregătirea dinaintea buclei nu se repetă), cronometrul pornește la primul Loop și se oprește la ultimul (pregătirea și curățenia nu sunt măsurate, deci nu mai ai nevoie de b.ResetTimer), iar compilatorul nu poate elimina apelurile din buclă ca „inutile”.

Nu folosi b.N în interiorul unei bucle b.Loop și nu le amesteca în același benchmark.

Ce returnează

true cât timp trebuie să mai ruleze, false când măsurătoarea s-a terminat.

Exemple

Exemplul 1: Pregătire în afara buclei

Lista de 100 000 de prețuri se creează o singură dată și nu intră în timpul măsurat.

// fișierul sortare_test.go
package magazin

import (
    "slices"
    "testing"
)

func BenchmarkSortare(b *testing.B) {
    preturi := make([]int, 100_000)
    for i := range preturi {
        preturi[i] = (i * 7919) % 100_000 // date amestecate, fără aleatoriu
    }
    copie := make([]int, len(preturi))

    for b.Loop() {
        copy(copie, preturi)
        slices.Sort(copie)
    }
}

Rezultatul în terminal, pe un Mac cu procesor Apple (numerele diferă de la un calculator la altul)

$ go test -bench=Sortare
goos: darwin
goarch: arm64
pkg: magazin
cpu: Apple M1 Pro
BenchmarkSortare-10    	     242	   4951476 ns/op
PASS
ok  	magazin	1.504s

Exemplul 2: Bucla veche cu b.N, pentru comparație

Același calcul în stilul de dinainte de Go 1.24: b.ResetTimer() este necesar ca pregătirea să nu fie măsurată, iar funcția este apelată de mai multe ori, cu b.N tot mai mare.

// fișierul sortare_veche_test.go
package magazin

import (
    "slices"
    "testing"
)

func BenchmarkSortareVeche(b *testing.B) {
    preturi := make([]int, 100_000)
    for i := range preturi {
        preturi[i] = (i * 7919) % 100_000
    }
    copie := make([]int, len(preturi))
    b.ResetTimer() // fără el, pregătirea ar fi măsurată

    for i := 0; i < b.N; i++ {
        copy(copie, preturi)
        slices.Sort(copie)
    }
}

Rezultatul în terminal, pe un Mac cu procesor Apple (numerele diferă de la un calculator la altul)

$ go test -bench=SortareVeche
goos: darwin
goarch: arm64
pkg: magazin
cpu: Apple M1 Pro
BenchmarkSortareVeche-10    	     241	   5314792 ns/op
PASS
ok  	magazin	2.114s

Greșeli frecvente

  • Scrii for i := 0; i < b.N; i++ în cod nou. b.Loop e mai simplu și mai greu de folosit greșit.
  • Apelezi b.Loop() în afara condiției unui for sau de două ori pe iterație. Măsurătoarea devine greșită.

Comenzi înrudite

  • testing.B: Tipul primit de funcțiile benchmark, care măsoară cât durează (și câtă memorie folosește) o bucată de cod.
  • testing.B.ResetTimer: Pune la zero timpul și alocările măsurate până acum, ca pregătirea costisitoare să nu intre în rezultat.