testing.B în Go

Tipul primit de funcțiile benchmark, care măsoară cât durează (și câtă memorie folosește) o bucată de cod.

Sintaxă
// în fișierul nume_test.go
func BenchmarkNume(b *testing.B) {
    for b.Loop() {
        // codul măsurat
    }
}

Ce face testing.B

Un benchmark este o funcție func BenchmarkXxx(b *testing.B) într-un fișier _test.go. Nu rulează la go test obișnuit, ci doar cu go test -bench=. (sau un model în loc de punct). Go repetă codul de câte ori e nevoie ca măsurătoarea să fie stabilă și raportează timpul mediu pe operație (ns/op).

Bucla de măsurare se scrie cu for b.Loop() { ... } (Go 1.24). Forma veche, for i := 0; i < b.N; i++ { ... }, se găsește în mult cod existent. Cu -benchmem sau b.ReportAllocs() vezi și memoria alocată pe operație. testing.B are și metodele lui testing.T (Error, Fatal, Log, Run).

Rezultatele depind de calculator; compară doar măsurători făcute pe aceeași mașină. Unealta benchstat compară statistic două rulări.

Parametri

Parametru Descriere
b Valoarea primită de benchmark.

Ce returnează

Nu se aplică (este un tip).

Exemple

Exemplul 1: Concatenare cu + față de strings.Builder

// fișierul text_test.go
package magazin

import (
    "strings"
    "testing"
)

var produse = strings.Fields("pâine lapte ouă unt brânză mere pere cafea ceai zahăr")

func BenchmarkPlus(b *testing.B) {
    for b.Loop() {
        s := ""
        for _, p := range produse {
            s += p + ", "
        }
    }
}

func BenchmarkBuilder(b *testing.B) {
    for b.Loop() {
        var sb strings.Builder
        for _, p := range produse {
            sb.WriteString(p)
            sb.WriteString(", ")
        }
        _ = sb.String()
    }
}

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

$ go test -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: magazin
cpu: Apple M1 Pro
BenchmarkPlus-10       	 3921576	       306.5 ns/op	     432 B/op	      10 allocs/op
BenchmarkBuilder-10    	 8212881	       144.3 ns/op	     248 B/op	       5 allocs/op
PASS
ok  	magazin	2.621s

Exemplul 2: Un benchmark rulat din main cu testing.Benchmark

testing.Benchmark rulează un benchmark dintr-un program obișnuit. Timpul diferă la fiecare rulare, așa că afișăm doar dacă măsurătoarea s-a făcut.

package main

import (
    "fmt"
    "strings"
    "testing"
)

func main() {
    r := testing.Benchmark(func(b *testing.B) {
        for b.Loop() {
            strings.Repeat("Iași ", 100)
        }
    })
    fmt.Println("a rulat de mai multe ori:", r.N > 1)
    fmt.Println("timp pe operație măsurat:", r.NsPerOp() > 0)
}

Rezultat

a rulat de mai multe ori: true
timp pe operație măsurat: true

Greșeli frecvente

  • Rulezi go test și te miri că benchmark-urile nu apar. Adaugă -bench=..
  • Tragi concluzii dintr-o singură rulare sau compari rezultate de pe calculatoare diferite. Rulează de mai multe ori (-count=10) și compară cu benchstat.

Comenzi înrudite

  • testing.B.Loop: Condiția buclei unui benchmark: returnează true cât timp mai trebuie repetat codul măsurat (Go 1.24).
  • testing.B.ResetTimer: Pune la zero timpul și alocările măsurate până acum, ca pregătirea costisitoare să nu intre în rezultat.
  • testing.T: Tipul primit de fiecare funcție de test; prin el raportezi erori, scrii mesaje și controlezi testul.