Los benchmarks en Go son funciones del paquete testing que miden el rendimiento de tu código de forma reproducible. Al contrario que un simple time.Now() manual, el framework de benchmarks controla el número de iteraciones, calienta el código, descarta ejecuciones anómalas e informa de nanosegundos por operación y bytes por operación.
Estructura de un benchmark
// fichero: algoritmo_test.go
package algoritmo_test
import (
"testing"
"ejemplo.com/miapp/algoritmo"
)
func BenchmarkBusquedaLineal(b *testing.B) {
datos := generarDatos(10000)
b.ResetTimer() // excluye generarDatos del tiempo medido
for i := 0; i < b.N; i++ {
algoritmo.BusquedaLineal(datos, 9999)
}
}
func BenchmarkBusquedaBinaria(b *testing.B) {
datos := generarDatosOrdenados(10000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
algoritmo.BusquedaBinaria(datos, 9999)
}
}
La clave es el bucle for i := 0; i < b.N; i++. El framework ajusta b.N automáticamente: comienza con 1 y va aumentando hasta que la duración total supera el tiempo mínimo (por defecto 1 segundo). Esto garantiza resultados estables.
Ejecutar benchmarks
# -bench acepta un patrón regex (. = todos los benchmarks) go test -bench=. ./... # solo benchmarks que coincidan con "Busqueda" go test -bench=Busqueda ./... # con información de memoria go test -bench=. -benchmem ./... # número de iteraciones fijo go test -bench=. -benchtime=5s ./... go test -bench=. -benchtime=100x ./... # exactamente 100 iteraciones
Interpretar los resultados
BenchmarkBusquedaLineal-8 1000000 1234 ns/op 0 B/op 0 allocs/op BenchmarkBusquedaBinaria-8 5000000 213 ns/op 0 B/op 0 allocs/op
-8 número de CPUs usadas (GOMAXPROCS)1000000 número de iteraciones que realizó b.N1234 ns/op nanosegundos por operación0 B/op bytes asignados en heap por operación (con -benchmem)0 allocs/op número de allocations por operación (con -benchmem)
ResetTimer: excluir el setup del tiempo
func BenchmarkConSetup(b *testing.B) {
// este setup puede tardar; no queremos medirlo
cache := cargarCacheDesdeDB(1_000_000)
b.ResetTimer() // el tiempo empieza a contar aquí
for i := 0; i < b.N; i++ {
cache.Get("clave-ejemplo")
}
}
Comparar implementaciones
func BenchmarkConcatenarPlus(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ""
for j := 0; j < 100; j++ {
s += "x"
}
_ = s
}
}
func BenchmarkConcatenarBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
var sb strings.Builder
for j := 0; j < 100; j++ {
sb.WriteString("x")
}
_ = sb.String()
}
}
BenchmarkConcatenarPlus-8 100000 15432 ns/op 5280 B/op 99 allocs/op BenchmarkConcatenarBuilder-8 1000000 1021 ns/op 224 B/op 3 allocs/op
La diferencia en allocations explica la diferencia de rendimiento: concatenar con + en un bucle crea una nueva cadena en cada iteración.
pprof: profiling detallado
# genera un perfil de CPU go test -bench=. -cpuprofile=cpu.out ./... go tool pprof cpu.out # genera un perfil de memoria go test -bench=. -memprofile=mem.out ./... go tool pprof mem.out # interfaz web interactiva go tool pprof -http=:8080 cpu.out
pprof muestra qué funciones consumen más tiempo CPU o asignan más memoria, lo que te guía para saber dónde optimizar.
