Benchmarks y pruebas de rendimiento

Avanzado
Go
Go
Actualizado: 03/04/2026

¿Qué es un benchmark en Go?

Un benchmark es una prueba que mide cuánto tiempo tarda una operación en ejecutarse. Go incluye soporte de benchmarks en el mismo paquete testing, sin dependencias externas.

Benchmarks en Go: b.Loop, benchmem, perfiles con pprof y comparación

Las funciones de benchmark siguen la convención BenchmarkNombre(b *testing.B):

func BenchmarkSumar(b *testing.B) {
    for b.Loop() {
        Sumar(2, 3)
    }
}

El método b.Loop() es la forma idiomática de escribir benchmarks en Go. El framework controla automáticamente el número de iteraciones hasta obtener una medida estable. Ejecuta con:

go test -bench=.                    # todos los benchmarks
go test -bench=BenchmarkSumar       # uno específico
go test -bench=. -benchtime=5s      # ejecutar durante 5 segundos
go test -bench=. -count=3           # repetir 3 veces para estadísticas

Salida de un benchmark

BenchmarkSumar-8    1000000000    0.2895 ns/op

| Campo | Significado | |-------|-------------| | BenchmarkSumar-8 | Nombre del benchmark, -8 son los GOMAXPROCS | | 1000000000 | Iteraciones ejecutadas (b.N) | | 0.2895 ns/op | Nanosegundos por operación |

Medir asignaciones de memoria: -benchmem

go test -bench=. -benchmem
BenchmarkConcatenar-8    5000000    234 ns/op    64 B/op    2 allocs/op

| Campo | Significado | |-------|-------------| | 64 B/op | Bytes asignados por operación | | 2 allocs/op | Número de asignaciones heap por operación |

Reducir allocs/op es clave para mejorar el rendimiento, ya que menos asignaciones significan menos presión sobre el recolector de basura.

Aislar el código medido: b.ResetTimer y b.StopTimer

Cuando el setup consume tiempo que no debe incluirse en la medición:

func BenchmarkProcesarDatos(b *testing.B) {
    // Preparación costosa fuera del bucle
    datos := generarDatosGrandes()

    b.ResetTimer() // reiniciar el temporizador tras la preparación
    for b.Loop() {
        ProcesarDatos(datos)
    }
}

func BenchmarkConIO(b *testing.B) {
    for b.Loop() {
        b.StopTimer()
        fichero := crearFicheroTemp(b)
        b.StartTimer()

        LeerFichero(fichero)
    }
}

Benchmarks paralelos con b.RunParallel

Para medir rendimiento en escenarios concurrentes:

func BenchmarkHandler(b *testing.B) {
    servidor := httptest.NewServer(miHandler())
    defer servidor.Close()

    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            resp, _ := http.Get(servidor.URL + "/api/datos")
            resp.Body.Close()
        }
    })
}

Perfiles con pprof

Para análisis profundo de CPU y memoria:

# Generar perfil de CPU durante benchmarks
go test -bench=. -cpuprofile=cpu.prof

# Generar perfil de memoria
go test -bench=. -memprofile=mem.prof

# Analizar perfiles
go tool pprof cpu.prof

Dentro de la herramienta interactiva:

(pprof) top10        # las 10 funciones que más CPU consumen
(pprof) web          # visualización gráfica en el navegador
(pprof) list Funcion # código anotado con tiempos

Comparar benchmarks con benchstat

benchstat detecta diferencias estadísticamente significativas entre dos runs:

go install golang.org/x/perf/cmd/benchstat@latest

# Guardar resultados de dos versiones
go test -bench=. -count=5 > antes.txt
# (hacer cambios en el código)
go test -bench=. -count=5 > despues.txt

benchstat antes.txt despues.txt

Salida ejemplo:

name         old time/op   new time/op   delta
Procesar-8   234ns ± 2%    187ns ± 1%   -20.09%  (p=0.000 n=5+5)

Optimizaciones comunes detectables con benchmarks

// Mala práctica: concatenación de strings en bucle
func concatenarMal(partes []string) string {
    resultado := ""
    for _, p := range partes {
        resultado += p  // nueva asignación en cada iteración
    }
    return resultado
}

// Buena práctica: usar strings.Builder
func concatenarBien(partes []string) string {
    var sb strings.Builder
    for _, p := range partes {
        sb.WriteString(p)
    }
    return sb.String()
}

Los benchmarks mostrarán claramente la diferencia en allocs/op.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Go

Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Escribir funciones de benchmark con *testing.B.
  • Comprender el ciclo de b.N y su gestión automática por el framework.
  • Medir asignaciones de memoria con -benchmem.
  • Usar b.ResetTimer y b.StopTimer para aislar el código medido.
  • Generar perfiles de CPU y memoria con pprof.
  • Comparar benchmarks con benchstat para detectar regresiones.