Go-Performance: Wie man Code profiliert und beschleunigt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Ihr Go-Code läuft langsam? Zeit, unter die Haube zu schauen

\n\n

Sie haben einen Dienst in Go geschrieben. Er funktioniert. Aber wenn die Last 10.000 RPS erreicht, beginnt der Server zu „keuchen". Kommt Ihnen das bekannt vor? Go ist bekannt für seine Leistung „out of the box", aber selbst in dieser eleganten Sprache gibt es Fallstricke. Goroutine-Leaks, ineffizientes Speichermanagement, unnötige Allokationen – all das tötet die Geschwindigkeit.

\n\n

In diesem Artikel schauen wir uns an, wie Go-Profiling hilft, Engpässe zu finden, und zeigen konkrete Techniken zur Optimierung von Go-Anwendungen. Sie erfahren, wie Sie mit den integrierten Tools pprof und go test -bench Ihre Anwendung in eine Rakete verwandeln. Los geht's!

\n\n

Werkzeugkasten des Profilers: pprof und trace

\n\n

Go bietet zwei leistungsstarke Werkzeuge zur Leistungsanalyse: pprof (Profiling von CPU, Speicher, Sperren) und go tool trace (Analyse von Nebenläufigkeit und GC-Ereignissen).

\n\n

pprof in den HTTP-Server einbetten

\n\n

Der einfachste Weg, mit dem Profiling zu beginnen, ist das Hinzufügen von net/http/pprof zu Ihrem Server. Das erfordert genau eine Importzeile:

\n\n
import (\n    _ "net/http/pprof"\n    "net/http"\n)\n\nfunc main() {\n    http.ListenAndServe(":8080", nil)\n}
\n\n

Jetzt sind die Profile unter /debug/pprof/ verfügbar. Um ein CPU-Profil für 30 Sekunden aufzunehmen, führen Sie aus:

\n\n
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
\n\n

Nach der Datenerfassung startet eine interaktive Konsole. Der Befehl top15 zeigt die heißesten Funktionen:

\n\n
(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\n

Sehen Sie main.heavyComputation? Das ist unser Kandidat für die Optimierung. Zur Visualisierung verwenden Sie web (falls Graphviz installiert ist) – Sie erhalten einen Aufrufgraphen.

\n\n

Speicher-Profiling: Leaks finden

\n\n

Speicherlecks in Go sind meistens Goroutine-Leaks oder vergessene Referenzen auf große Objekte. Nehmen wir ein Heap-Profil auf:

\n\n
go tool pprof http://localhost:8080/debug/pprof/heap
\n\n

Der Befehl alloc_objects zeigt, welche Funktionen die meisten Objekte erstellen:

\n\n
(pprof) alloc_objects\n    2.50GB  100%  100%      2.50GB  100%  main.createLargeSlice
\n\n

So haben wir eine Funktion gefunden, die 2,5 GB Speicher allokiert! Möglicherweise wird dort ein riesiges Slice erstellt, das mit sync.Pool wiederverwendet werden kann.

\n\n

Benchmarks schreiben: Ihr erster Schritt zur Optimierung

\n\n

Bevor Sie optimieren, müssen Sie messen. Go-Benchmarks sind das Standardwerkzeug zur Leistungsmessung einzelner Funktionen.

\n\n

Beispiel: String-Verkettung vergleichen

\n\n

Angenommen, wir verketten Strings in einer Schleife. Schlechte Variante:

\n\n
func ConcatSlow(parts []string) string {\n    result := ""\n    for _, p := range parts {\n        result += p\n    }\n    return result\n}
\n\n

Gute Variante – mit strings.Builder:

\n\n
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

Schreiben wir einen Benchmark (Datei concat_test.go):

\n\n
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\n

Starten:

\n\n
go test -bench=. -benchmem
\n\n

Ergebnis:

\n\n
BenchmarkConc                

Blogs

Buchempfehlungen