# Go Profiling e Benchmarking 2026: pprof, trace e Domande per Colloqui > Guida completa al profiling Go con pprof, execution tracing e benchmark profiling. Include domande pratiche per colloqui sull'ottimizzazione delle performance in Go. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Tags: go, profiling, benchmarking, performance, pprof, colloquio - Reading time: 12 min --- Il profiling in Go con pprof e il pacchetto `runtime/trace` trasforma le supposizioni in dati concreti. Le domande sulla performance compaiono nella maggior parte dei colloqui Go, e i candidati che sanno leggere un flame graph o spiegare quando usare `-inuse_space` rispetto a `-allocs` si distinguono nettamente. Gli strumenti sono inclusi nella libreria standard, non richiedono dipendenze esterne e si integrano direttamente con i benchmark. > **Tipi di Profilo da Conoscere** > > Go 1.24+ fornisce sette profili integrati: CPU, heap, allocs, goroutine, threadcreate, block e mutex. Go 1.26 ha aggiunto un profilo sperimentale per i leak di goroutine che rileva goroutine bloccate non raggiungibili. ## CPU Profiling con pprof: Il Punto di Partenza Il CPU profiling campiona lo stack delle chiamate a intervalli regolari (default 100 Hz) e registra quali funzioni consumano tempo processore. Il pacchetto `runtime/pprof` gestisce la raccolta a basso livello, mentre `go tool pprof` analizza i risultati. Un profilo di 30 secondi cattura 3000 campioni, sufficienti per la significatività statistica nella maggior parte delle applicazioni. Un programma standalone abilita il profiling chiamando `pprof.StartCPUProfile` all'avvio. Il profilo viene scritto su un file che `go tool pprof` legge successivamente: ```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 } ``` Per i server HTTP, basta importare `net/http/pprof` come side effect. Il pacchetto registra automaticamente gli handler su `/debug/pprof/`. Non sono necessarie altre modifiche al codice: ```go // server.go package main import ( "net/http" _ "net/http/pprof" // Registers /debug/pprof/* handlers ) func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } ``` Per ottenere un profilo CPU di 30 secondi da un server in esecuzione: `go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30`. Lo strumento scarica il profilo e apre una shell interattiva. Il parametro `seconds` controlla la durata della raccolta. ## Analisi dei Profili: top, list e Flame Graph La shell pprof fornisce comandi per identificare i colli di bottiglia. `top` mostra le funzioni che consumano più CPU, ordinate per tempo flat. Il comando `list` visualizza il codice sorgente con annotazioni temporali per riga, localizzando esattamente le righe che dominano l'esecuzione. ```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) ... ``` L'interfaccia web aggiunge analisi visuale. Eseguendo `go tool pprof -http=:6060 cpu.prof` si apre un browser con flame graph, grafi diretti e viste del sorgente. Da Go 1.26, i flame graph sono la vista predefinita nella UI web. I flame graph mostrano la gerarchia delle chiamate orizzontalmente, con barre piu larghe che indicano piu tempo trascorso in quella funzione e nelle sue chiamate. > **Leggere i Flame Graph** > > In un flame graph, l'asse X rappresenta la popolazione dei campioni, non il tempo. Ogni box e una funzione, e la sua larghezza mostra quanto spesso quella funzione appariva nei campioni. Le funzioni parent si trovano sotto le chiamate figlie. Cercare ampi plateau in cima: quelle funzioni svolgono il lavoro effettivo. ## Memory Profiling: Heap vs Allocs Il memory profiling risponde a due domande distinte. Il profilo **heap** (`-inuse_space`) mostra cosa trattiene memoria al momento della cattura. Il profilo **allocs** mostra dove si sono verificate le allocazioni nel tempo, anche se quella memoria e stata liberata. Per ridurre l'uso di memoria corrente, si esamina il profilo heap. Per ridurre il tasso di allocazione e la pressione sul GC, si esamina il profilo allocs. Alti tassi di allocazione attivano garbage collection frequenti, che mettono in pausa le goroutine e aumentano l'uso della 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 ``` Il flag `-inuse_objects` conta gli oggetti vivi invece dei byte, utile per identificare la frammentazione della memoria. Il flag `-alloc_space` mostra i byte totali allocati durante il periodo del profilo, rivelando funzioni che consumano memoria velocemente anche se la rilasciano rapidamente. Hotspot di allocazione comuni includono la concatenazione di stringhe nei loop (usare `strings.Builder`), conversioni di interface che escapano sull'heap, e crescita degli slice senza preallocazione. La [documentazione del compilatore Go](https://go.dev/doc/diagnostics#profiling) spiega l'escape analysis in dettaglio. ## Benchmark Profiling con testing.B Il pacchetto `testing` integra il profiling direttamente nei benchmark. Questa combinazione isola percorsi di codice specifici senza il rumore di un'applicazione completa. Il benchmark profiling risponde alla domanda: "Come performa questa funzione in isolamento?" ```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) } } ``` I profili vengono generati durante l'esecuzione dei benchmark tramite flag. I flag `-cpuprofile` e `-memprofile` scrivono i profili su file per analisi successive: ```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 ``` Il flag `-benchtime` controlla quanto tempo eseguire il benchmark. Esecuzioni piu lunghe producono profili piu accurati ma richiedono piu tempo. Un'esecuzione di 5 secondi fornisce tipicamente risultati stabili. Per i micro-benchmark, usare `-count=10` per eseguire iterazioni multiple e verificare la varianza. ## Execution Tracing con runtime/trace Mentre pprof mostra dove viene speso il tempo, `runtime/trace` mostra quando si verificano gli eventi. Il trace cattura lo scheduling delle goroutine, le system call, gli eventi GC e l'attivita di rete su una timeline. Questa visibilita nel comportamento della concurrency complementa il profiling statistico. ```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() } ``` Il visualizzatore trace mostra i tempi di vita delle goroutine, gli eventi di blocking e l'utilizzo dei processori. Ogni goroutine appare come una barra orizzontale, con colori che indicano se stava eseguendo, era bloccata o aspettava lo scheduling: ```bash $ go tool trace trace.out # Opens browser at http://127.0.0.1:port ``` Per i server HTTP, ottenere un trace da `/debug/pprof/trace?seconds=5`. Il visualizzatore trace mostra quali goroutine erano bloccate su cosa, rivelando pattern di contention che i profili CPU non rilevano. La vista "Goroutine analysis" raggruppa le goroutine per punto di creazione, aiutando a identificare leak o fan-out inaspettati. I trace sono piu pesanti dei profili. Un trace di 5 secondi di un server occupato puo produrre centinaia di megabyte di dati. Usare durate brevi e raccolta mirata. Il [blog Go sull'execution tracing](https://go.dev/blog/execution-traces-2024) copre tecniche di analisi avanzate. ## Block e Mutex Profiling per la Contention Il block profiling registra le goroutine in attesa su primitive di sincronizzazione: channel, mutex e condition variable. Il mutex profiling si concentra specificamente sulla contention dei mutex. Questi profili rivelano colli di bottiglia di concurrency invisibili al CPU profiling. Abilitare questi profili impostando parametri runtime prima che si verifichi la contention: ```go // Enable block profiling (1 = sample all blocking events) runtime.SetBlockProfileRate(1) // Enable mutex profiling (1 = sample all mutex contention) runtime.SetMutexProfileFraction(1) ``` Per la produzione, impostare valori piu alti per ridurre l'overhead. Un block profile rate di 1000000 (un microsecondo) o una mutex fraction di 100 fornisce dati utili con impatto minimo. Valori troppo bassi catturano ogni evento e possono rallentare l'applicazione. Ottenere questi profili dagli endpoint standard: ```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 ``` I block profile mostrano il tempo totale di attesa, non il numero di eventi di blocking. Una funzione che blocca per 1 secondo una volta appare identica a una che blocca per 1 millisecondo 1000 volte. L'execution tracing distingue questi casi. ## Errori Comuni nel Profiling Il profiling introduce overhead che puo distorcere i risultati. Il CPU profiling aggiunge circa il 5% di overhead. Il memory profiling campiona le allocazioni (1 ogni 512KB di default), quindi allocazioni piccole potrebbero non apparire. Il tracing cattura ogni evento e puo aggiungere il 10-30% di overhead. Diversi errori portano a profili fuorvianti: **Profilare build ottimizzate diversamente.** Profilare sempre con gli stessi flag di build usati in produzione. Le build di debug disabilitano inlining e ottimizzazioni, facendo apparire gli hot spot in posti diversi. **Profilare sotto carico artificiale.** Un profilo di un server inattivo mostra il loop idle, non i veri colli di bottiglia. Profilare sotto pattern di traffico realistici. **Ignorare l'overhead del GC.** I profili CPU includono il tempo speso in garbage collection. Un'alta presenza di `runtime.gc*` indica problemi di allocazione memoria, non problemi CPU. Affrontarli con il memory profiling. **Durate di profiling brevi.** Un profilo di 1 secondo cattura solo 100 campioni. Il rumore statistico domina. Profilare per almeno 30 secondi sotto carico costante. ## Domande di Colloquio Go sul Profiling Le domande sulla performance testano se un candidato sa diagnosticare problemi reali. Gli intervistatori cercano familiarita con gli strumenti e comprensione di cosa rivela ogni profilo. **D: Quando l'heap profiling mostra risultati diversi dall'allocs profiling?** Heap mostra la memoria trattenuta al momento della cattura. Allocs mostra tutte le allocazioni, inclusa la memoria liberata. Una funzione che alloca buffer temporanei in un loop appare in allocs ma non in heap se i buffer vengono raccolti prima dello snapshot. Usare allocs per ridurre la pressione sul GC, heap per trovare i leak. **D: Una goroutine sembra bloccata. Quale profilo aiuta?** Il profilo goroutine mostra gli stack trace di tutte le goroutine. Il profilo block mostra dove le goroutine attendono. Per Go 1.26+, il profilo sperimentale di leak goroutine rileva goroutine non raggiungibili bloccate su channel o mutex. L'execution tracing mostra la timeline degli eventi di blocking. **D: Cosa significa percentuale flat versus percentuale cumulativa in pprof?** Flat misura il tempo nella funzione stessa. Cumulative include il tempo nelle funzioni chiamate. Una funzione con alta cumulativa ma bassa flat e un coordinatore che delega il lavoro. Una funzione con alta flat fa il calcolo effettivo. Ottimizzare prima le funzioni con alta flat. **D: Come profilare un benchmark senza profilare il setup del test?** Chiamare `b.ResetTimer()` dopo il completamento del setup. Per benchmark con setup per iterazione, usare `b.StopTimer()` e `b.StartTimer()` attorno al codice di setup. Le chiamate timer hanno overhead di nanosecondi, quindi evitarle nei loop stretti. **D: Perche una funzione potrebbe non apparire in un profilo CPU nonostante sia lenta?** Il CPU profiling cattura solo funzioni che usano attivamente la CPU. Funzioni I/O-bound (in attesa su rete, disco o channel) appaiono nei block profile o trace, non nei CPU profile. Il sampling puo anche perdere funzioni che eseguono per meno di 10ms in totale. **D: Come influisce l'implementazione Swiss Tables map di Go 1.24 sul profiling?** Go 1.24 ha sostituito la map basata su bucket con Swiss Tables, riducendo l'overhead CPU del 2-3% per workload pesanti di map. I profili presi prima e dopo l'upgrade mostrano stack di chiamate relativi alle map differenti. Il flag `GOEXPERIMENT=noswissmap` ritorna alla vecchia implementazione per confronti. ## Continuous Profiling in Produzione I profili puntuali perdono problemi transitori. Strumenti di continuous profiling come [Pyroscope](https://pyroscope.io/) o [Parca](https://www.parca.dev/) raccolgono campioni a basso overhead continuamente, permettendo confronti tra deployment. Questi strumenti correlano i profili con metriche e trace. I profili integrati di Go funzionano con questi strumenti tramite il formato pprof. Il servizio [pprof.me](https://pprof.me/) ha aggiunto funzionalita di confronto nel 2026, permettendo upload e diff di profili per quantificare l'impatto delle ottimizzazioni prima e dopo le modifiche al codice. Per la [preparazione ai colloqui Go](/technologies/go/interview-questions/testing), e importante comprendere sia gli strumenti che i concetti sottostanti. Il [pacchetto context](/technologies/go/interview-questions/context-package) e i [pattern di concurrency](/technologies/go/interview-questions/concurrency-patterns) appaiono frequentemente insieme alle domande sul profiling. L'ottimizzazione delle performance spesso richiede la combinazione di dati di profiling con la conoscenza del comportamento del runtime Go. ## Cosa Rivela il Go Profiling sul Comportamento delle Applicazioni - I profili CPU identificano le funzioni hot ma perdono il lavoro I/O-bound. Combinare con trace per il quadro completo. - I profili memoria distinguono problemi di retention (heap) da churn di allocazione (allocs). Usare `-inuse_space` per i leak, `-alloc_space` per la pressione sul GC. - I profili block e mutex espongono contention che i profili CPU non possono vedere. Abilitarli quando la latenza aumenta sotto carico. - Il benchmark profiling isola percorsi di codice specifici. Chiamare sempre `b.ReportAllocs()` e `b.ResetTimer()` per misurazioni accurate. - Il visualizzatore trace mostra lo scheduling delle goroutine e gli eventi di blocking su una timeline, essenziale per diagnosticare bug di concurrency. - I miglioramenti del runtime Go 1.24 hanno ridotto l'overhead CPU del 2-3% tramite Swiss Tables map e una nuova implementazione mutex. - Il profiling in produzione con gli endpoint `/debug/pprof/` richiede autenticazione. Mai esporre questi endpoint pubblicamente: rivelano lo stato interno dell'applicazione e possono abilitare denial-of-service tramite raccolta costosa di profili. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/go/go-profiling-benchmarking-pprof-trace-interview