# Profiling y Benchmarking en Go 2026: pprof, trace y Preguntas de Entrevista > Dominar el profiling en Go con pprof y runtime/trace. Técnicas de análisis de CPU, memoria y goroutines para optimización de rendimiento y preparación de entrevistas. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- El profiling en Go con pprof y el paquete `runtime/trace` convierte las suposiciones en datos concretos. Las preguntas sobre rendimiento aparecen en la mayoría de las entrevistas de Go, y los candidatos capaces de interpretar un flame graph o explicar cuándo usar `-inuse_space` versus `-allocs` destacan sobre los demás. El conjunto de herramientas viene incluido en la biblioteca estándar, no requiere dependencias externas y se integra directamente con los benchmarks. > **Tipos de Perfiles que Hay que Conocer** > > Go 1.24+ proporciona siete perfiles integrados: CPU, heap, allocs, goroutine, threadcreate, block y mutex. Go 1.26 agregó un perfil experimental de fugas de goroutines que detecta goroutines bloqueadas inalcanzables. ## Profiling de CPU con pprof: El Punto de Partida El profiling de CPU muestrea la pila de llamadas a intervalos regulares (100 Hz por defecto) y registra qué funciones consumen tiempo de procesador. El paquete `runtime/pprof` maneja la recolección de bajo nivel, mientras que `go tool pprof` analiza los resultados. Un perfil de 30 segundos captura 3000 muestras, suficiente para significancia estadística en la mayoría de las aplicaciones. Un programa independiente habilita el profiling llamando a `pprof.StartCPUProfile` al inicio. El perfil se escribe en un archivo que `go tool pprof` lee posteriormente: ```go // main.go package main import ( "flag" "log" "os" "runtime/pprof" ) var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") func main() { flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } defer f.Close() pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // Application logic here } ``` Para servidores HTTP, importar `net/http/pprof` como efecto secundario es suficiente. El paquete registra handlers automáticamente en `/debug/pprof/`. No se necesitan cambios de código más allá del import: ```go // server.go package main import ( "net/http" _ "net/http/pprof" // Registers /debug/pprof/* handlers ) func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } ``` Obtener un perfil de CPU de 30 segundos desde un servidor en ejecución con `go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30`. La herramienta descarga el perfil y abre una shell interactiva. El parámetro `seconds` controla la duración de la recolección. ## Analizando Perfiles: top, list y Flame Graphs La shell de pprof proporciona comandos para identificar cuellos de botella. `top` muestra las funciones que consumen más tiempo de CPU, ordenadas por tiempo flat. El comando `list` muestra el código fuente con anotaciones de tiempo por línea, identificando exactamente las líneas que dominan la ejecución. ```bash # Terminal session with go tool pprof $ go tool pprof cpu.prof (pprof) top 10 Showing nodes accounting for 4.2s, 85% of 4.9s total flat flat% sum% cum cum% 1.8s 36.73% 36.73% 1.8s 36.73% runtime.memmove 0.9s 18.37% 55.10% 0.9s 18.37% encoding/json.(*decodeState).scanWhile 0.5s 10.20% 65.30% 2.3s 46.94% main.processRecords ... (pprof) list processRecords Total: 4.9s 0.5s 2.3s (flat, cum) 46.94% of Total 20: for _, r := range records { 21: 0.3s 0.3s data := json.Marshal(r) 22: 0.2s 2.0s result := transform(data) ... ``` La interfaz web agrega análisis visual. Ejecutar `go tool pprof -http=:6060 cpu.prof` abre un navegador con flame graphs, grafos dirigidos y vistas de código fuente. Desde Go 1.26, los flame graphs aparecen como vista predeterminada en la interfaz web. Los flame graphs muestran la jerarquía de llamadas horizontalmente, con barras más anchas indicando más tiempo dedicado a esa función y sus llamadas. > **Interpretando Flame Graphs** > > En un flame graph, el eje x representa la población de muestras, no el tiempo. Cada caja es una función, y su ancho muestra con qué frecuencia esa función apareció en las muestras. Las funciones padre se sitúan debajo de sus hijos. Buscar mesetas anchas en la parte superior: esas funciones realizan el trabajo real. ## Profiling de Memoria: Heap vs Allocs El profiling de memoria responde dos preguntas distintas. El perfil **heap** (`-inuse_space`) muestra qué retiene memoria en el momento de la captura. El perfil **allocs** muestra dónde ocurrieron las asignaciones a lo largo del tiempo, incluso si esa memoria ya fue liberada. Para reducir el uso de memoria actual, examinar el perfil heap. Para reducir la tasa de asignación y la presión del GC, examinar el perfil allocs. Tasas de asignación altas disparan recolecciones de basura frecuentes, que pausan goroutines y aumentan el uso de CPU. ```bash # Capture heap profile from running server $ curl -o heap.prof http://localhost:8080/debug/pprof/heap $ go tool pprof -inuse_space heap.prof # Capture allocs profile (allocation count over 30s) $ curl -o allocs.prof "http://localhost:8080/debug/pprof/allocs?seconds=30" $ go tool pprof -alloc_objects allocs.prof ``` El flag `-inuse_objects` cuenta objetos vivos en lugar de bytes, útil para identificar fragmentación de memoria. El flag `-alloc_space` muestra el total de bytes asignados durante el período del perfil, revelando funciones que consumen memoria aunque la liberen rápidamente. Los puntos calientes de asignación comunes incluyen concatenación de strings en bucles (usar `strings.Builder`), conversiones de interface que escapan al heap, y crecimiento de slices sin preasignación. La [documentación del compilador Go](https://go.dev/doc/diagnostics#profiling) explica el análisis de escape en detalle. ## Profiling de Benchmarks con testing.B El paquete `testing` integra el profiling directamente en los benchmarks. Esta combinación aísla rutas de código específicas sin el ruido de una aplicación completa. El profiling de benchmark responde la pregunta: "¿Cómo se desempeña esta función en aislamiento?" ```go // parser_test.go package parser import "testing" func BenchmarkParseJSON(b *testing.B) { data := []byte(`{"id":1,"name":"test","values":[1,2,3]}`) b.ReportAllocs() // Include allocation stats b.ResetTimer() // Exclude setup from timing for i := 0; i < b.N; i++ { _, _ = Parse(data) } } ``` Generar perfiles durante la ejecución del benchmark con flags. Los flags `-cpuprofile` y `-memprofile` escriben perfiles a archivos para análisis posterior: ```bash # CPU profile during benchmark $ go test -bench=BenchmarkParseJSON -cpuprofile=cpu.prof -benchtime=5s # Memory profile during benchmark $ go test -bench=BenchmarkParseJSON -memprofile=mem.prof -benchtime=5s # Analyze the result $ go tool pprof -http=:6060 cpu.prof ``` El flag `-benchtime` controla cuánto tiempo se ejecuta el benchmark. Ejecuciones más largas producen perfiles más precisos pero toman más tiempo. Una ejecución de 5 segundos típicamente proporciona resultados estables. Para micro-benchmarks, usar `-count=10` para ejecutar múltiples iteraciones y verificar la varianza. ## Trazado de Ejecución con runtime/trace Mientras pprof muestra dónde se gasta el tiempo, `runtime/trace` muestra cuándo ocurren los eventos. La traza captura la planificación de goroutines, llamadas al sistema, eventos del GC y actividad de red en una línea temporal. Esta visibilidad del comportamiento concurrente complementa el profiling estadístico. ```go // trace_example.go package main import ( "os" "runtime/trace" ) func main() { f, _ := os.Create("trace.out") defer f.Close() trace.Start(f) defer trace.Stop() // Application logic runConcurrentTasks() } ``` El visualizador de trazas muestra las duraciones de vida de las goroutines, eventos de bloqueo y utilización del procesador. Cada goroutine aparece como una barra horizontal, con colores indicando si se ejecutaba, estaba bloqueada o esperando planificación: ```bash $ go tool trace trace.out # Opens browser at http://127.0.0.1:port ``` Para servidores HTTP, obtener una traza desde `/debug/pprof/trace?seconds=5`. El visualizador de trazas muestra qué goroutines estaban bloqueadas en qué, revelando patrones de contención que los perfiles de CPU no detectan. La vista "Goroutine analysis" agrupa goroutines por sitio de creación, ayudando a identificar fugas o fan-out inesperados. Las trazas son más pesadas que los perfiles. Una traza de 5 segundos de un servidor ocupado puede producir cientos de megabytes de datos. Usar duraciones cortas y recolección dirigida. El [blog de Go sobre trazado de ejecución](https://go.dev/blog/execution-traces-2024) cubre técnicas de análisis avanzadas. ## Profiling de Block y Mutex para Contención El profiling de block registra goroutines esperando en primitivas de sincronización: channels, mutexes y variables de condición. El profiling de mutex se enfoca específicamente en la contención de mutex. Estos perfiles revelan cuellos de botella de concurrencia invisibles al profiling de CPU. Habilitar estos perfiles estableciendo parámetros de runtime antes de que ocurra la contención: ```go // Enable block profiling (1 = sample all blocking events) runtime.SetBlockProfileRate(1) // Enable mutex profiling (1 = sample all mutex contention) runtime.SetMutexProfileFraction(1) ``` Para producción, establecer valores más altos para reducir el overhead. Una tasa de perfil de block de 1000000 (un microsegundo) o fracción de mutex de 100 proporciona datos útiles con impacto mínimo. Establecer estos valores demasiado bajos captura cada evento y puede ralentizar la aplicación. Obtener estos perfiles desde los endpoints estándar: ```bash $ curl -o block.prof http://localhost:8080/debug/pprof/block $ curl -o mutex.prof http://localhost:8080/debug/pprof/mutex $ go tool pprof block.prof ``` Los perfiles de block muestran el tiempo total esperando, no el número de eventos de bloqueo. Una función que bloquea por 1 segundo una vez se ve idéntica a una que bloquea por 1 milisegundo 1000 veces. Usar trazado de ejecución para distinguir estos casos. ## Errores Comunes del Profiling El profiling introduce overhead que puede sesgar los resultados. El profiling de CPU agrega aproximadamente 5% de overhead. El profiling de memoria muestrea asignaciones (1 por 512KB por defecto), así que las asignaciones pequeñas pueden no aparecer. El trazado captura cada evento y puede agregar 10-30% de overhead. Varios errores llevan a perfiles engañosos: **Perfilar builds optimizados de manera diferente.** Siempre perfilar con los mismos flags de build usados en producción. Los builds de debug deshabilitan el inlining y las optimizaciones, haciendo que los puntos calientes aparezcan en lugares diferentes. **Perfilar bajo carga artificial.** Un perfil de un servidor inactivo muestra el bucle de inactividad, no los cuellos de botella reales. Perfilar bajo patrones de tráfico realistas. **Ignorar el overhead del GC.** Los perfiles de CPU incluyen tiempo gastado en recolección de basura. Una presencia alta de `runtime.gc*` indica problemas de asignación de memoria, no problemas de CPU. Abordarlos con profiling de memoria. **Duraciones de profiling cortas.** Un perfil de 1 segundo captura solo 100 muestras. El ruido estadístico domina. Perfilar por al menos 30 segundos bajo carga estable. ## Preguntas de Entrevista de Go sobre Profiling Las preguntas de rendimiento prueban si un candidato puede diagnosticar problemas reales. Los entrevistadores buscan familiaridad con las herramientas y comprensión de lo que revela cada perfil. **P: ¿Cuándo mostraría el profiling de heap resultados diferentes al profiling de allocs?** Heap muestra la memoria retenida al momento de la captura. Allocs muestra todas las asignaciones, incluyendo memoria liberada. Una función que asigna buffers temporales en un bucle aparece en allocs pero no en heap si los buffers son recolectados antes del snapshot. Usar allocs para reducir la presión del GC, heap para encontrar fugas. **P: Una goroutine parece bloqueada. ¿Qué perfil ayuda?** El perfil de goroutine muestra los stack traces de todas las goroutines. El perfil de block muestra dónde esperan las goroutines. Para Go 1.26+, el perfil experimental de fugas de goroutines detecta goroutines inalcanzables bloqueadas en channels o mutexes. El trazado de ejecución muestra la línea temporal de eventos de bloqueo. **P: ¿Qué significa un porcentaje flat versus un porcentaje acumulativo en pprof?** Flat mide tiempo en la función misma. Acumulativo incluye tiempo en funciones que llama. Una función con acumulativo alto pero flat bajo es un coordinador que delega trabajo. Una función con flat alto hace el cómputo real. Optimizar primero las funciones con flat alto. **P: ¿Cómo perfilar un benchmark sin perfilar el setup del test?** Llamar a `b.ResetTimer()` después de que el setup termine. Para benchmarks con setup por iteración, usar `b.StopTimer()` y `b.StartTimer()` alrededor del código de setup. Las llamadas al timer tienen overhead de nanosegundos, así que evitarlas en bucles ajustados. **P: ¿Por qué una función podría no aparecer en un perfil de CPU a pesar de ser lenta?** El profiling de CPU solo captura funciones usando activamente CPU. Las funciones limitadas por I/O (esperando red, disco o channels) aparecen en perfiles de block o trazas, no en perfiles de CPU. El muestreo también puede omitir funciones que se ejecutan menos de 10ms en total. **P: ¿Cómo afecta la implementación de mapas Swiss Tables de Go 1.24 al profiling?** Go 1.24 reemplazó el mapa basado en buckets con Swiss Tables, reduciendo el overhead de CPU en 2-3% para cargas de trabajo intensivas en mapas. Los perfiles tomados antes y después de la actualización muestran diferentes pilas de llamadas relacionadas con mapas. El flag `GOEXPERIMENT=noswissmap` revierte a la implementación anterior para comparación. ## Profiling Continuo en Producción Los perfiles puntuales omiten problemas transitorios. Las herramientas de profiling continuo como [Pyroscope](https://pyroscope.io/) o [Parca](https://www.parca.dev/) recolectan muestras de bajo overhead continuamente, permitiendo comparación entre deployments. Estas herramientas correlacionan perfiles con métricas y trazas. Los perfiles integrados de Go funcionan con estas herramientas a través del formato pprof. El servicio [pprof.me](https://pprof.me/) agregó funcionalidades de comparación en 2026, permitiendo cargar y hacer diff de perfiles para cuantificar el impacto de optimizaciones antes y después de cambios de código. Para la [preparación de entrevistas de Go](/technologies/go/interview-questions/testing), entender tanto las herramientas como los conceptos subyacentes importa. El [paquete context](/technologies/go/interview-questions/context-package) y los [patrones de concurrencia](/technologies/go/interview-questions/concurrency-patterns) aparecen frecuentemente junto con preguntas de profiling. La optimización de rendimiento a menudo requiere combinar datos de profiling con conocimiento del comportamiento del runtime de Go. ## Lo que el Profiling de Go Revela sobre el Comportamiento de las Aplicaciones - Los perfiles de CPU identifican funciones calientes pero omiten trabajo limitado por I/O. Combinar con traza para la imagen completa. - Los perfiles de memoria distinguen problemas de retención (heap) del churn de asignación (allocs). Usar `-inuse_space` para fugas, `-alloc_space` para presión del GC. - Los perfiles de block y mutex exponen contención que los perfiles de CPU no pueden ver. Habilitarlos cuando la latencia tiene picos bajo carga. - El profiling de benchmark aísla rutas de código específicas. Siempre llamar a `b.ReportAllocs()` y `b.ResetTimer()` para mediciones precisas. - El visualizador de trazas muestra la planificación de goroutines y eventos de bloqueo en una línea temporal, esencial para diagnosticar bugs de concurrencia. - Las mejoras del runtime de Go 1.24 redujeron el overhead de CPU en 2-3% a través de mapas Swiss Tables y una nueva implementación de mutex. - El profiling en producción con los endpoints `/debug/pprof/` requiere autenticación. Nunca exponer estos endpoints públicamente: filtran estado interno de la aplicación y pueden habilitar denegación de servicio a través de recolección de perfiles costosa. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/go/go-profiling-benchmarking-pprof-trace-interview