Votre code Go est lent ? Il est temps de regarder sous le capot
\n\nVous avez écrit un service en Go. Il fonctionne. Mais lorsque la charge atteint 10 000 RPS, le serveur commence à « s'essouffler ». Une situation familière ? Go est réputé pour ses performances « prêtes à l'emploi », mais même ce langage élégant a ses pièges. Les fuites de goroutines, une gestion inefficace de la mémoire, des allocations superflues – tout cela tue la vitesse.
\n\nDans cet article, nous allons voir comment le profilage Go aide à trouver les goulots d'étranglement, et montrer des techniques concrètes d'optimisation d'applications Go. Vous apprendrez comment, grâce aux outils intégrés pprof et go test -bench, transformer votre application en fusée. C'est parti !
\n\nLa boîte à outils du profileur : pprof et trace
\n\nGo fournit deux outils puissants pour l'analyse des performances : pprof (profilage CPU, mémoire, verrous) et go tool trace (analyse de la concurrence et des événements GC).
Intégrer pprof dans un serveur HTTP
\n\nLa façon la plus simple de commencer le profilage est d'ajouter net/http/pprof à votre serveur. Cela prend exactement une ligne d'import :
import (\n _ "net/http/pprof"\n "net/http"\n)\n\nfunc main() {\n http.ListenAndServe(":8080", nil)\n}\n\nMaintenant, les profils sont disponibles à l'adresse /debug/pprof/. Pour capturer un profil CPU pendant 30 secondes, exécutez :
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30\n\nAprès la collecte des données, une console interactive se lance. La commande top15 affiche les fonctions les plus gourmandes :
(pprof) top15\nShowing nodes accounting for 3.50s, 87.50% of 4.00s total\n flat flat% sum% cum cum%\n 1.20s 30.00% 30.00% 1.50s 37.50% main.heavyComputation\n\nVous voyez main.heavyComputation ? C'est notre candidat à l'optimisation. Pour la visualisation, utilisez web (si Graphviz est installé) – vous obtiendrez un graphe d'appels.
Profilage mémoire : rechercher les fuites
\n\nLes fuites mémoire en Go sont le plus souvent des fuites de goroutines ou des références oubliées à de gros objets. Capturons un profil heap :
\n\ngo tool pprof http://localhost:8080/debug/pprof/heap\n\nLa commande alloc_objects montre quelles fonctions créent le plus d'objets :
(pprof) alloc_objects\n 2.50GB 100% 100% 2.50GB 100% main.createLargeSlice\n\nVoilà comment nous avons trouvé une fonction qui alloue 2,5 Go de mémoire ! Peut-être qu'elle crée une énorme slice qui pourrait être réutilisée avec sync.Pool.
Écrire des benchmarks : votre premier pas vers l'optimisation
\n\nAvant d'optimiser, il faut mesurer. Les benchmarks Go sont l'outil standard pour mesurer les performances de fonctions individuelles.
\n\nExemple : comparer la concaténation de chaînes
\n\nSupposons que nous concaténions des chaînes dans une boucle. La mauvaise version :
\n\nfunc ConcatSlow(parts []string) string {\n result := ""\n for _, p := range parts {\n result += p\n }\n return result\n}\n\nLa bonne version – utiliser strings.Builder :
func ConcatFast(parts []string) string {\n var b strings.Builder\n for _, p := range parts {\n b.WriteString(p)\n }\n return b.String()\n}\n\nÉcrivons un benchmark (fichier concat_test.go) :
func BenchmarkConcatSlow(b *testing.B) {\n parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n for i := 0; i < b.N; i++ {\n ConcatSlow(parts)\n }\n}\n\nfunc BenchmarkConcatFast(b *testing.B) {\n parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n for i := 0; i < b.N; i++ {\n ConcatFast(parts)\n }\n}\n\nLançons :
\n\ngo test -bench=. -benchmem\n\nRésultat :
\n\nBenchmarkConc