# Profilage et Benchmarking Go en 2026 : pprof, trace et Questions d'Entretien > Maîtriser le profilage Go avec pprof et runtime/trace. Techniques d'analyse CPU, mémoire et goroutines pour l'optimisation des performances et la préparation aux entretiens. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Le profilage Go avec pprof et le package `runtime/trace` transforme les suppositions en données exploitables. Les questions sur les performances apparaissent dans la plupart des entretiens Go, et les candidats capables de lire un flame graph ou d'expliquer quand utiliser `-inuse_space` versus `-allocs` se démarquent. L'outillage est fourni avec la bibliothèque standard, ne nécessite aucune dépendance externe et s'intègre directement aux benchmarks. > **Types de Profils à Connaître** > > Go 1.24+ fournit sept profils intégrés : CPU, heap, allocs, goroutine, threadcreate, block et mutex. Go 1.26 a ajouté un profil expérimental de fuite de goroutines qui détecte les goroutines bloquées inaccessibles. ## Profilage CPU avec pprof : Le Point de Départ Le profilage CPU échantillonne la pile d'appels à intervalles réguliers (100 Hz par défaut) et enregistre quelles fonctions consomment du temps processeur. Le package `runtime/pprof` gère la collecte de bas niveau, tandis que `go tool pprof` analyse les résultats. Un profil de 30 secondes capture 3000 échantillons, suffisant pour une signification statistique dans la plupart des applications. Un programme autonome active le profilage en appelant `pprof.StartCPUProfile` au démarrage. Le profil s'écrit dans un fichier que `go tool pprof` lit ensuite : ```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 } ``` Pour les serveurs HTTP, importer `net/http/pprof` comme effet de bord suffit. Le package enregistre automatiquement des handlers à `/debug/pprof/`. Aucune modification de code au-delà de l'import n'est nécessaire : ```go // server.go package main import ( "net/http" _ "net/http/pprof" // Registers /debug/pprof/* handlers ) func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } ``` Récupérer un profil CPU de 30 secondes depuis un serveur en cours d'exécution avec `go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30`. L'outil télécharge le profil et ouvre un shell interactif. Le paramètre `seconds` contrôle la durée de collecte. ## Analyser les Profils : top, list et Flame Graphs Le shell pprof fournit des commandes pour identifier les goulots d'étranglement. `top` affiche les fonctions consommant le plus de temps CPU, triées par temps flat. La commande `list` affiche le code source avec des annotations de timing par ligne, localisant précisément les lignes qui dominent l'exécution. ```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'interface web ajoute une analyse visuelle. Exécuter `go tool pprof -http=:6060 cpu.prof` ouvre un navigateur avec des flame graphs, des graphes orientés et des vues source. Depuis Go 1.26, les flame graphs apparaissent comme vue par défaut dans l'interface web. Les flame graphs montrent la hiérarchie des appels horizontalement, avec des barres plus larges indiquant plus de temps passé dans cette fonction et ses appelées. > **Lecture des Flame Graphs** > > Dans un flame graph, l'axe x représente la population d'échantillons, pas le temps. Chaque boîte est une fonction, et sa largeur montre à quelle fréquence cette fonction apparaît dans les échantillons. Les fonctions parentes se situent sous leurs enfants. Rechercher les plateaux larges au sommet : ces fonctions effectuent le travail réel. ## Profilage Mémoire : Heap vs Allocs Le profilage mémoire répond à deux questions distinctes. Le profil **heap** (`-inuse_space`) montre ce qui retient la mémoire au moment de la capture. Le profil **allocs** montre où les allocations se sont produites au fil du temps, même si cette mémoire a depuis été libérée. Pour réduire l'utilisation mémoire actuelle, examiner le profil heap. Pour réduire le taux d'allocation et la pression GC, examiner le profil allocs. Des taux d'allocation élevés déclenchent des collectes de déchets fréquentes, qui suspendent les goroutines et augmentent l'utilisation 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 ``` Le flag `-inuse_objects` compte les objets vivants plutôt que les octets, utile pour identifier la fragmentation mémoire. Le flag `-alloc_space` montre le total d'octets alloués sur la période du profil, révélant les fonctions qui consomment de la mémoire même si elles la libèrent rapidement. Les points chauds d'allocation courants incluent la concaténation de chaînes dans les boucles (utiliser `strings.Builder`), les conversions d'interface qui s'échappent vers le heap, et la croissance de slices sans préallocation. La [documentation du compilateur Go](https://go.dev/doc/diagnostics#profiling) explique l'analyse d'échappement en détail. ## Profilage de Benchmarks avec testing.B Le package `testing` intègre le profilage directement dans les benchmarks. Cette combinaison isole des chemins de code spécifiques sans le bruit d'une application complète. Le profilage de benchmark répond à la question : "Comment cette fonction performe-t-elle en isolation ?" ```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) } } ``` Générer des profils pendant l'exécution du benchmark avec des flags. Les flags `-cpuprofile` et `-memprofile` écrivent les profils dans des fichiers pour analyse ultérieure : ```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 ``` Le flag `-benchtime` contrôle la durée d'exécution du benchmark. Des exécutions plus longues produisent des profils plus précis mais prennent plus de temps. Une exécution de 5 secondes fournit généralement des résultats stables. Pour les micro-benchmarks, utiliser `-count=10` pour exécuter plusieurs itérations et vérifier la variance. ## Traçage d'Exécution avec runtime/trace Alors que pprof montre où le temps est passé, `runtime/trace` montre quand les événements se produisent. La trace capture la planification des goroutines, les appels système, les événements GC et l'activité réseau sur une timeline. Cette visibilité sur le comportement concurrent complète le profilage statistique. ```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() } ``` Le visualiseur de trace affiche les durées de vie des goroutines, les événements de blocage et l'utilisation des processeurs. Chaque goroutine apparaît comme une barre horizontale, avec des couleurs indiquant si elle s'exécutait, était bloquée ou attendait la planification : ```bash $ go tool trace trace.out # Opens browser at http://127.0.0.1:port ``` Pour les serveurs HTTP, récupérer une trace depuis `/debug/pprof/trace?seconds=5`. Le visualiseur de trace montre quelles goroutines étaient bloquées sur quoi, révélant des patterns de contention que les profils CPU manquent. La vue "Goroutine analysis" regroupe les goroutines par site de création, aidant à identifier les fuites ou les fan-out inattendus. Les traces sont plus lourdes que les profils. Une trace de 5 secondes d'un serveur occupé peut produire des centaines de mégaoctets de données. Utiliser des durées courtes et une collecte ciblée. Le [blog Go sur le traçage d'exécution](https://go.dev/blog/execution-traces-2024) couvre les techniques d'analyse avancées. ## Profilage Block et Mutex pour la Contention Le profilage block enregistre les goroutines en attente sur des primitives de synchronisation : channels, mutex et variables de condition. Le profilage mutex se concentre spécifiquement sur la contention de mutex. Ces profils révèlent des goulots d'étranglement de concurrence invisibles au profilage CPU. Activer ces profils en définissant des paramètres runtime avant que la contention ne se produise : ```go // Enable block profiling (1 = sample all blocking events) runtime.SetBlockProfileRate(1) // Enable mutex profiling (1 = sample all mutex contention) runtime.SetMutexProfileFraction(1) ``` Pour la production, définir des valeurs plus élevées pour réduire l'overhead. Un taux de profil block de 1000000 (une microseconde) ou une fraction mutex de 100 fournit des données utiles avec un impact minimal. Définir ces valeurs trop basses capture chaque événement et peut ralentir l'application. Récupérer ces profils depuis les endpoints 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 ``` Les profils block montrent le temps total passé à attendre, pas le nombre d'événements de blocage. Une fonction qui bloque pendant 1 seconde une fois semble identique à une qui bloque pendant 1 milliseconde 1000 fois. Utiliser le traçage d'exécution pour distinguer ces cas. ## Pièges Courants du Profilage Le profilage introduit un overhead qui peut fausser les résultats. Le profilage CPU ajoute environ 5% d'overhead. Le profilage mémoire échantillonne les allocations (1 par 512KB par défaut), donc les petites allocations peuvent ne pas apparaître. Le traçage capture chaque événement et peut ajouter 10-30% d'overhead. Plusieurs erreurs mènent à des profils trompeurs : **Profiler des builds optimisés différemment.** Toujours profiler avec les mêmes flags de build utilisés en production. Les builds debug désactivent l'inlining et les optimisations, faisant apparaître les points chauds à différents endroits. **Profiler sous charge artificielle.** Un profil d'un serveur inactif montre la boucle d'inactivité, pas les vrais goulots d'étranglement. Profiler sous des patterns de trafic réalistes. **Ignorer l'overhead GC.** Les profils CPU incluent le temps passé dans la collecte des déchets. Une présence élevée de `runtime.gc*` indique des problèmes d'allocation mémoire, pas des problèmes CPU. Les adresser avec le profilage mémoire. **Durées de profilage courtes.** Un profil d'1 seconde capture seulement 100 échantillons. Le bruit statistique domine. Profiler pendant au moins 30 secondes sous charge stable. ## Questions d'Entretien Go sur le Profilage Les questions de performance testent si un candidat peut diagnostiquer de vrais problèmes. Les recruteurs recherchent la familiarité avec l'outillage et la compréhension de ce que chaque profil révèle. **Q : Quand le profilage heap montrerait-il des résultats différents du profilage allocs ?** Heap montre la mémoire retenue au moment de la capture. Allocs montre toutes les allocations, y compris la mémoire libérée. Une fonction qui alloue des buffers temporaires dans une boucle apparaît dans allocs mais pas dans heap si les buffers sont collectés avant le snapshot. Utiliser allocs pour réduire la pression GC, heap pour trouver les fuites. **Q : Une goroutine semble bloquée. Quel profil aide ?** Le profil goroutine montre les traces de pile de toutes les goroutines. Le profil block montre où les goroutines attendent. Pour Go 1.26+, le profil expérimental de fuite de goroutines détecte les goroutines inaccessibles bloquées sur des channels ou mutex. Le traçage d'exécution montre la timeline des événements de blocage. **Q : Que signifie un pourcentage flat versus un pourcentage cumulatif dans pprof ?** Flat mesure le temps dans la fonction elle-même. Cumulatif inclut le temps dans les fonctions qu'elle appelle. Une fonction avec un cumulatif élevé mais un flat bas est un coordinateur qui délègue le travail. Une fonction avec un flat élevé fait le calcul réel. Optimiser d'abord les fonctions avec un flat élevé. **Q : Comment profiler un benchmark sans profiler le setup de test ?** Appeler `b.ResetTimer()` après que le setup soit terminé. Pour les benchmarks avec setup par itération, utiliser `b.StopTimer()` et `b.StartTimer()` autour du code de setup. Les appels timer ont un overhead en nanosecondes, donc les éviter dans les boucles serrées. **Q : Pourquoi une fonction pourrait-elle ne pas apparaître dans un profil CPU malgré sa lenteur ?** Le profilage CPU capture uniquement les fonctions utilisant activement le CPU. Les fonctions I/O-bound (en attente de réseau, disque ou channels) apparaissent dans les profils block ou les traces, pas les profils CPU. L'échantillonnage peut aussi manquer les fonctions qui s'exécutent moins de 10ms au total. **Q : Comment l'implémentation Swiss Tables map de Go 1.24 affecte-t-elle le profilage ?** Go 1.24 a remplacé la map basée sur des buckets par Swiss Tables, réduisant l'overhead CPU de 2-3% pour les charges de travail intensives en map. Les profils pris avant et après la mise à niveau montrent différentes piles d'appels liées aux maps. Le flag `GOEXPERIMENT=noswissmap` revient à l'ancienne implémentation pour comparaison. ## Profilage Continu en Production Les profils ponctuels manquent les problèmes transitoires. Les outils de profilage continu comme [Pyroscope](https://pyroscope.io/) ou [Parca](https://www.parca.dev/) collectent des échantillons à faible overhead en continu, permettant la comparaison entre les déploiements. Ces outils corrèlent les profils avec les métriques et les traces. Les profils intégrés de Go fonctionnent avec ces outils via le format pprof. Le service [pprof.me](https://pprof.me/) a ajouté des fonctionnalités de comparaison en 2026, permettant l'upload et le diff de profils pour quantifier l'impact des optimisations avant et après les changements de code. Pour la [préparation aux entretiens Go](/technologies/go/interview-questions/testing), comprendre à la fois l'outillage et les concepts sous-jacents est important. Le [package context](/technologies/go/interview-questions/context-package) et les [patterns de concurrence](/technologies/go/interview-questions/concurrency-patterns) apparaissent fréquemment aux côtés des questions de profilage. L'optimisation des performances nécessite souvent de combiner les données de profilage avec la connaissance du comportement du runtime Go. ## Ce que le Profilage Go Révèle sur le Comportement des Applications - Les profils CPU identifient les fonctions chaudes mais manquent le travail I/O-bound. Combiner avec la trace pour l'image complète. - Les profils mémoire distinguent les problèmes de rétention (heap) du churn d'allocation (allocs). Utiliser `-inuse_space` pour les fuites, `-alloc_space` pour la pression GC. - Les profils block et mutex exposent la contention que les profils CPU ne peuvent pas voir. Les activer quand la latence spike sous charge. - Le profilage de benchmark isole des chemins de code spécifiques. Toujours appeler `b.ReportAllocs()` et `b.ResetTimer()` pour des mesures précises. - Le visualiseur de trace montre la planification des goroutines et les événements de blocage sur une timeline, essentiel pour diagnostiquer les bugs de concurrence. - Les améliorations du runtime Go 1.24 ont réduit l'overhead CPU de 2-3% grâce aux maps Swiss Tables et une nouvelle implémentation de mutex. - Le profilage en production avec les endpoints `/debug/pprof/` nécessite une authentification. Ne jamais exposer ces endpoints publiquement : ils fuient l'état interne de l'application et peuvent permettre un déni de service via une collecte de profils coûteuse. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/go/go-profiling-benchmarking-pprof-trace-interview