Anleitung: Go-Anwendung optimieren

In dieser Anleitung stellen Sie eine bewusst ineffiziente Go-Anwendung bereit, die für die Erfassung von Profildaten konfiguriert ist. Über die Profiler-Oberfläche rufen Sie die Profildaten auf und ermitteln eine potenzielle Optimierung. Anschließend ändern Sie diese Anwendung, stellen sie bereit und bewerten die Auswirkungen der Änderung.

Hinweis

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Klicken Sie im Navigationsbereich der Google Cloud Console auf Profiler oder verwenden Sie die folgende Schaltfläche, um die Cloud Profiler API für Ihr Projekt zu aktivieren:

    Profiler öffnen

  4. Wenn Sie Cloud Shell öffnen möchten, klicken Sie in der Symbolleiste der Google Cloud Console auf Cloud Shell aktivieren:

    Screenshot: Cloud Shell aktivieren

    Nach kurzer Zeit wird in der Google Cloud Console eine Cloud Shell-Sitzung geöffnet:

    Screenshot: Cloud Shell-Sitzung

Beispielanwendung

Das Hauptziel ist es, die Anzahl der Abfragen pro Sekunde zu maximieren, die der Server verarbeiten kann. Ein weiteres Ziel ist es dabei, durch Reduzierung unnötiger Arbeitsspeicherzuweisungen die Speichernutzung zu optimieren.

Der Server empfängt mithilfe eines gRPC-Frameworks ein Wort oder eine Wortgruppe und gibt dann an, wie oft das Wort oder die Wortgruppe in den Werken von Shakespeare vorkommt.

Die durchschnittliche Anzahl von Abfragen pro Sekunde, die der Server verarbeiten kann, wird durch Lasttests des Servers bestimmt. Für jede Testrunde wird ein Clientsimulator aufgerufen und angewiesen, 20 sequentielle Abfragen auszuführen. Nach Abschluss einer Runde werden die Anzahl der vom Clientsimulator gesendeten Abfragen, die benötigte Zeit und die durchschnittliche Anzahl der Abfragen pro Sekunde angezeigt.

Der Servercode ist absichtlich ineffizient.

Beispielanwendung ausführen

Laden Sie die Beispielanwendung herunter und führen Sie sie aus:

  1. Führen Sie in Cloud Shell die folgenden Befehle aus:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    cd golang-samples/profiler/shakesapp
    
  2. Führen Sie die Anwendung aus, nachdem Sie die Version auf 1 und die Anzahl der Runden auf 15 gesetzt haben:

    go run . -version 1 -num_rounds 15
    

    Nach ein oder zwei Minuten werden Profildaten angezeigt: Die Profildaten sehen etwa wie im folgenden Beispiel aus:

    Screenshot: anfängliches Flame-Diagramm für die CPU-Zeitnutzung

    In der Abbildung sehen Sie, dass für Profiltyp der Wert CPU time festgelegt ist. Das bedeutet, dass im Flame-Diagramm CPU-Nutzungsdaten angezeigt werden.

    Im Folgenden sehen Sie ein Beispiel für eine in Cloud Shell ausgegebene Ausgabe:

    $ go run . -version 1 -num_rounds 15
    2020/08/27 17:27:34 Simulating client requests, round 1
    2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 17:27:34 profiler has started
    2020/08/27 17:27:34 creating a new profile via profiler service
    2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec
    2020/08/27 17:27:51 Simulating client requests, round 2
    2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec
    2020/08/27 17:28:10 Simulating client requests, round 3
    2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec
    ...
    2020/08/27 17:44:32 Simulating client requests, round 14
    2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec
    2020/08/27 17:46:04 Simulating client requests, round 15
    2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
    

    In der Cloud Shell-Ausgabe werden die benötigte Zeit für jede Iteration und die durchschnittliche Anforderungsrate angezeigt: Wenn die Anwendung gestartet wird, weist der Eintrag "Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec" darauf hin, dass der Server etwa eine Anfrage pro Sekunde ausführt. In der letzten Runde bedeutet der Eintrag "Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec", dass der Server alle 5 Sekunden etwa eine Anfrage ausführt.

CPU-Zeitprofile zur Maximierung der Anzahl an Abfragen pro Sekunde verwenden

Eine Möglichkeit zur Maximierung der Anzahl der Abfragen pro Sekunde besteht darin, CPU-intensive Methoden zu ermitteln und deren Implementierung zu optimieren. In diesem Abschnitt verwenden Sie CPU-Zeitprofile, um eine CPU-intensive Methode auf dem Server zu ermitteln.

Nutzung der CPU-Zeit ermitteln

Der Stammframe des Flame-Diagramms listet die gesamte von der Anwendung verwendete CPU-Zeit während des Erfassungsintervall von 10 Sekunden auf:

Screenshot: erweiterte Ansicht des Flame-Diagramm-Stamm-Frames

In diesem Beispiel hat der Dienst 2.37 s verwendet. Wenn das System auf einem einzigen Kern ausgeführt wird, entspricht die CPU-Zeitnutzung von 2,37 Sekunden der Nutzung von 23,7 % dieses Kerns. Weitere Informationen finden Sie unter Verfügbare Arten der Profilerstellung.

Anwendung ändern

Änderung beurteilen

So bewerten Sie die Änderung:

  1. Führen Sie die Anwendung aus, nachdem Sie die Anwendungsversion auf 2 gesetzt haben:

    go run . -version 2 -num_rounds 40
    

    In einem Abschnitt weiter unten wird gezeigt, dass durch die Optimierung die für die Ausführung einer einzelnen Runde benötigte Zeit deutlich geringer ist als bei der unveränderten Anwendung. Damit die Anwendung lange genug ausgeführt wird, um Profile zu erfassen und hochzuladen, wird die Anzahl der Runden erhöht.

  2. Warten Sie, bis das Ausführen der Anwendung abgeschlossen ist, und sehen Sie sich dann die Profildaten für diese Version der Anwendung an:

    • Klicken Sie auf JETZT, um die neuesten Profildaten zu laden. Weitere Informationen dazu finden Sie unter Zeitraum.
    • Wählen Sie im Menü Version die Option 2 aus.

Für unser Beispiel sieht das Flame-Diagramm so aus:

Flame-Diagramm mit der CPU-Zeitnutzung von Version 2.

In dieser Abbildung wird im Stamm-Frame der Wert 7.8 s angezeigt. Aufgrund der Änderung der Stringübereinstimmungsfunktion erhöht sich die von der Anwendung verwendete CPU-Zeit von 2,37 Sekunden auf 7,8 Sekunden oder die Anwendung nutzte statt 23,7 % eines CPU-Kerns 78 % eines CPU-Kerns.

Die Frame-Breite ist ein proportionales Maß für die Nutzung der CPU-Zeit. In diesem Beispiel gibt die Breite des Frames für GetMatchCount an, dass die Funktion ungefähr 49 % der gesamten von der Anwendung genutzten CPU-Zeit beansprucht. Im ursprünglichen Flame-Diagramm füllte dieser Frame etwa 72 % der Breite des Diagramms aus. Zur Anzeige der exakten Nutzung der CPU-Zeit können Sie die Kurzinfo des Frames oder die Liste der Fokusfunktionen verwenden:

Focus function list mit der CPU-Zeitnutzung von Version 2.

Die Ausgabe in Cloud Shell zeigt, dass die geänderte Version etwa 5,8 Anfragen pro Sekunde abschließt:

$ go run . -version 2 -num_rounds 40
2020/08/27 18:21:40 Simulating client requests, round 1
2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618
2020/08/27 18:21:40 profiler has started
2020/08/27 18:21:40 creating a new profile via profiler service
2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec
2020/08/27 18:21:44 Simulating client requests, round 2
2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec
2020/08/27 18:21:47 Simulating client requests, round 3
2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec
...
2020/08/27 18:23:51 Simulating client requests, round 39
2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec
2020/08/27 18:23:54 Simulating client requests, round 40
2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec

Die einfache Änderung der Anwendung hatte zwei verschiedene Auswirkungen:

  • Die Anzahl der Anfragen pro Sekunde ist von unter 1 pro Sekunde auf 5,8 pro Sekunde gestiegen.

  • Die CPU-Zeit pro Anfrage, berechnet, indem die CPU-Auslastung durch die Anzahl der Anfragen pro Sekunde geteilt wird, ist auf 13,4% von 23,7 % gesunken.

    Beachten Sie, dass die CPU-Zeit pro Anfrage verringert hat, obwohl die CPU-Zeitnutzung von 2,37 Sekunden – dies entspricht einer Nutzung von 23,7% eines einzelnen CPU-Kerns – auf 7,8 Sekunden oder 78% eines CPU-Kerns angestiegen ist.

Mit zugewiesenen Heap-Profilen die Ressourcennutzung verbessern

In diesem Abschnitt wird beschrieben, wie Sie mithilfe des Heaps und der zugewiesenen Heap-Profile eine zuweisungsintensive Methode in der Anwendung identifizieren:

  • Heap-Profile zeigen den Arbeitsspeichermenge an, die im Heap des Programms zugewiesen ist, wenn das Profil erfasst wird.

  • Zugewiesene Heap-Profile geben den Gesamtarbeitsspeicher an, der während des Intervalls, in dem das Profil erfasst wurde, im Heap des Programms zugewiesen wurde. Wenn Sie diese Werte durch das Profilerfassungsintervall (10 Sekunden) teilen, können Sie diese als Zuordnungsraten interpretieren.

Heap-Profilerfassung aktivieren

  1. Führen Sie die Anwendung mit der Anwendungsversion gesetzt auf 3 aus und aktivieren Sie die Erfassung von Heap- und zugewiesenen Heap-Profilen.

    go run . -version 3 -num_rounds 40 -heap -heap_alloc
    
  2. Warten Sie, bis das Ausführen der Anwendung abgeschlossen ist, und sehen Sie sich dann die Profildaten für diese Version der Anwendung an:

    • Klicken Sie auf JETZT, um die neuesten Profildaten zu laden.
    • Wählen Sie im Menü Version die Option 3 aus.
    • Wählen Sie im Menü Profilertyp () die Option Zugewiesener Heap () aus.

    Für unser Beispiel sieht das Flame-Diagramm so aus:

    Flame-Diagramm von zugewiesenen Heap-Profilen für Version 3.

Heap-Zuweisungsrate ermitteln

Der Stamm-Frame zeigt die Gesamtmenge des Heap-Werts an, der in den zehn Sekunden bei der Erfassung des Profils zugewiesen wurde und über alle Profile gemittelt wird. In diesem Beispiel zeigt der Stamm-Frame, dass im Durchschnitt 1,535 GiB Arbeitsspeicher zugewiesen wurde.

Anwendung ändern

Änderung beurteilen

So bewerten Sie die Änderung:

  1. Führen Sie die Anwendung aus, nachdem Sie die Anwendungsversion auf 4 gesetzt haben:

    go run . -version 4 -num_rounds 60 -heap -heap_alloc
    
  2. Warten Sie, bis das Ausführen der Anwendung abgeschlossen ist, und sehen Sie sich dann die Profildaten für diese Version der Anwendung an:

    • Klicken Sie auf JETZT, um die neuesten Profildaten zu laden.
    • Wählen Sie im Menü Version die Option 4 aus.
    • Wählen Sie im Menü Profilertyp () die Option Zugewiesener Heap () aus.
  3. Um die Auswirkungen einer Änderung von readFiles auf die Heap-Zuweisungsrate zu quantifizieren, vergleichen Sie die zugewiesenen Heap-Profile für Version 4 mit denen, die für Version 3 erfasst wurden.

    Vergleich der zugewiesenen Heap-Profile zwischen den Versionen 4 und 3.

    Die Kurzinfo des Stamm-Frames zeigt, dass im Vergleich zu Version 4 die durchschnittliche während der Profilerfassung zugewiesene Speichermenge um 1,301 GiB verringert wurde. Die Kurzinfo für readFiles.func1 zeigt einen Rückgang von 1,045 GiB:

    Vergleich der Kurzinfo der gelesenen Dateien für den zugewiesenen Heap-Profiltyp.

  4. Um die Auswirkungen auf die automatische Speicherbereinigung zu quantifizieren, konfigurieren Sie einen Vergleich der CPU-Zeitprofile. In der folgenden Abbildung wird ein Filter angewendet, um die Stacks für die automatische Speicherbereinigung runtime.gcBgMarkWorker.* von Go darzustellen. Die Abbildung zeigt, dass die CPU-Nutzung für die automatische Speicherbereinigung von 16,8 % auf 4,97 % reduziert wurde.

    Vergleich der CPU-Zeitnutzung des Hintergrundspeicherbereinigungsprozesses von v4 bis v3.

  5. Sehen Sie sich die Ausgabe in Cloud Shell an, um festzustellen, ob sich die Änderung auf die Anzahl der von der Anwendung verarbeiteten Anfragen pro Sekunde auswirkt. In diesem Beispiel schließt Version 4 bis zu 15 Anfragen pro Sekunde ab, was wesentlich höher ist als die 5,8 Anfragen pro Sekunde von Version 3:

    $ go run . -version 4 -num_rounds 60 -heap -heap_alloc
    2020/08/27 21:51:42 Simulating client requests, round 1
    2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 21:51:42 profiler has started
    2020/08/27 21:51:42 creating a new profile via profiler service
    2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec
    2020/08/27 21:51:44 Simulating client requests, round 2
    2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec
    2020/08/27 21:51:45 Simulating client requests, round 3
    2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec
    ...
    

    Die Zunahme der Abfragen pro Sekunde, die von der Anwendung abgeschlossen werden, kann darauf zurückzuführen sein, dass weniger Zeit für die automatische Speicherbereinigung aufgewendet wird.

  • Sie können die Auswirkungen der Änderung auf readFiles besser nachvollziehen, wenn Sie die Heap-Profile prüfen. Ein Vergleich der Heap-Profile von Version 4 mit denen von Version 3 zeigt, dass die Heap-Nutzung von 70,95 MiB auf 18,47 MiB zurückgegangen ist:

    Vergleich der Heap-Nutzung von Version 4 mit Version 3.

Fazit

In dieser Kurzanleitung wurden CPU-Zeit und zugewiesene Heap-Profile verwendet, um eine potenzielle Optimierung für die Anwendung zu ermitteln. Das Ziel war es, die Anzahl der Anfragen pro Sekunde zu maximieren und unnötige Zuweisungen zu beseitigen.

  • Mithilfe von CPU-Zeitprofilen wurde eine CPU-intensive Funktion ermittelt. Nach einer einfachen Änderung hat sich die Anfragerate des Servers von etwa 1 Anfrage pro Sekunde auf 5,8 Anfragen pro Sekunde erhöht.

  • Unter Verwendung zugewiesener Heap-Profile wurde die shakesapp/server.go-Funktion readFiles als solche erkannt, die eine hohe Zuweisungsrate hat. Nach der Optimierung von readFiles hat die Anfragerate des Servers auf 15 Anfragen pro Sekunde erhöht und sich die durchschnittliche Speichermenge, die während der 10-Sekunden-Profilerfassung zugewiesen wurde, um 1,301 GiB verringert.

Nächste Schritte

  • Informationen dazu, wie Profile erfasst und an Ihr Google Cloud-Projekt gesendet werden, finden Sie unter Profilerfassung.

  • Lesen Sie unsere Ressourcen zu DevOps und gehen Sie unser Forschungsprogramm durch.

Weitere Informationen zum Ausführen des Cloud Profiler-Agents finden Sie unter: