Ottimizzare un'app Go

In questo tutorial, esegui il deployment di un'applicazione Go intenzionalmente inefficiente configurata per raccogliere dati del profilo. Utilizzi l'interfaccia di Profiler per visualizzare i dati del profilo e identificare le potenziali ottimizzazioni. Poi modifichi l'applicazione, la implementi e valuti l'effetto della modifica.

Prima di iniziare

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  4. Enable the required API.

    Enable the API

  5. Per aprire Cloud Shell, nella barra degli strumenti della console Google Cloud , fai clic su Attiva Cloud Shell:

    Attiva Cloud Shell.

    Dopo qualche istante, si apre una sessione di Cloud Shell all'interno della consoleGoogle Cloud :

    Sessione di Cloud Shell.

  6. Applicazione di esempio

    L'obiettivo principale è massimizzare il numero di query al secondo che il server può elaborare. Un obiettivo secondario è ridurre l'utilizzo della memoria eliminando le allocazioni di memoria non necessarie.

    Il server, utilizzando un framework gRPC, riceve una parola o una frase e restituisce il numero di volte in cui la parola o la frase compare nelle opere di Shakespeare.

    Il numero medio di query al secondo che il server può gestire viene determinato dal test di carico del server. Per ogni ciclo di test, viene chiamato un simulatore client e gli viene chiesto di eseguire 20 query sequenziali. Al termine di un round, vengono visualizzati il numero di query inviate dal simulatore client, il tempo trascorso e il numero medio di query al secondo.

    Il codice del server è intenzionalmente inefficiente.

    Esecuzione dell'applicazione di esempio

    Scarica ed esegui l'applicazione di esempio:

    1. In Cloud Shell, esegui questi comandi:

      git clone https://github.com/GoogleCloudPlatform/golang-samples.git
      cd golang-samples/profiler/shakesapp
      
    2. Esegui l'applicazione con la versione impostata su 1 e il numero di round impostato su 15:

      go run . -version 1 -num_rounds 15
      

      Dopo un minuto o due, vengono visualizzati i dati del profilo. I dati del profilo sono simili al seguente esempio:

      Grafico a fiamma iniziale per l'utilizzo del tempo della CPU.

      Nello screenshot, nota che il Tipo di profilo è impostato su CPU time. Ciò indica che i dati sull'utilizzo della CPU vengono visualizzati nel grafico a fiamme.

      Di seguito è riportato un output di esempio stampato in Cloud Shell:

      $ 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
      

      L'output di Cloud Shell mostra il tempo trascorso per ogni iterazione e la frequenza media delle richieste. Quando l'applicazione viene avviata, la voce "Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec" indica che il server esegue circa una richiesta al secondo. Nell'ultimo round, la voce "Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec" indica che il server esegue circa 1 richiesta ogni 5 secondi.

    Utilizzo dei profili di tempo della CPU per massimizzare le query al secondo

    Un approccio per massimizzare il numero di query al secondo consiste nell'identificare i metodi che richiedono un uso intensivo della CPU e ottimizzarne le implementazioni. In questa sezione, utilizzi i profili del tempo della CPU per identificare un metodo che utilizza molta CPU nel server.

    Identificare l'utilizzo del tempo CPU

    Il frame principale del grafico a fiamme elenca il tempo di CPU totale utilizzato dall'applicazione nell'intervallo di raccolta di 10 secondi:

    Visualizzazione espansa del frame radice del grafico a fiamme.

    In questo esempio, il servizio utilizzato è 2.37 s. Quando il sistema viene eseguito su un singolo core, un utilizzo del tempo della CPU di 2,37 secondi corrisponde al 23,7% di utilizzo di quel core. Per saperne di più, consulta la sezione Tipi di profilazione disponibili.

    Modificare l'applicazione

    Valutare la modifica

    Per valutare la modifica:

    1. Esegui l'applicazione con la versione dell'applicazione impostata su 2:

      go run . -version 2 -num_rounds 40
      

      Una sezione successiva mostra che con l'ottimizzazione, il tempo necessario per eseguire un singolo round è molto inferiore a quello dell'applicazione non modificata. Per garantire che l'applicazione venga eseguita per un periodo di tempo sufficiente a raccogliere e caricare i profili, il numero di round viene aumentato.

    2. Attendi il completamento dell'applicazione, quindi visualizza i dati del profilo per questa versione dell'applicazione:

      • Fai clic su ORA per caricare i dati del profilo più recenti. Per ulteriori informazioni, vedi Intervallo di tempo.
      • Nel menu Versione, seleziona 2.

    Ad esempio, il grafico a fiamma è il seguente:

    Grafico a fiamme che mostra l'utilizzo del tempo CPU della versione 2.

    In questa figura, il frame principale mostra un valore di 7.8 s. A seguito della modifica della funzione di corrispondenza delle stringhe, il tempo CPU utilizzato dall'applicazione è aumentato da 2,37 secondi a 7,8 secondi oppure l'applicazione è passata dall'utilizzo del 23,7% di un core della CPU all'utilizzo del 78% di un core della CPU.

    La larghezza del frame è una misura proporzionale dell'utilizzo del tempo della CPU. In questo esempio, la larghezza del frame per GetMatchCount indica che la funzione utilizza circa il 49% di tutto il tempo di CPU utilizzato dall'applicazione. Nel grafico a fiamma originale, questo stesso frame occupava circa il 72% della larghezza del grafico. Per visualizzare l'utilizzo esatto del tempo CPU, puoi utilizzare la descrizione comando del frame o l'elenco delle funzioni di messa a fuoco:

    Imposta lo stato attivo sull'elenco delle funzioni che mostra l'utilizzo del tempo CPU della versione 2.

    L'output in Cloud Shell mostra che la versione modificata completa circa 5,8 richieste al secondo:

    $ 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
    

    La piccola modifica all'applicazione ha avuto due effetti diversi:

    • Il numero di richieste al secondo è aumentato da meno di 1 al secondo a 5,8 al secondo.

    • Il tempo CPU per richiesta, calcolato dividendo l'utilizzo della CPU per il numero di richieste al secondo, è sceso dal 23,7% al 13,4%.

      Tieni presente che il tempo CPU per richiesta è diminuito anche se l'utilizzo del tempo CPU è aumentato da 2,37 secondi, che corrisponde al 23,7% di utilizzo di un singolo core della CPU, a 7,8 secondi, ovvero il 78% di un core della CPU.

    Utilizzo dei profili heap allocati per migliorare l'utilizzo delle risorse

    Questa sezione illustra come utilizzare i profili heap e heap allocato per identificare un metodo che richiede molte allocazioni nell'applicazione:

    • I profili heap mostrano la quantità di memoria allocata nell'heap del programma nell'istante in cui viene raccolto il profilo.

    • I profili heap allocato mostrano la quantità totale di memoria allocata nell'heap del programma durante l'intervallo in cui è stato raccolto il profilo. Dividendo questi valori per 10 secondi, l'intervallo di raccolta dei profili, puoi interpretarli come tassi di allocazione.

    Abilitazione della raccolta del profilo heap

    1. Esegui l'applicazione con la versione dell'applicazione impostata su 3 e attiva la raccolta dei profili heap e heap allocato.

      go run . -version 3 -num_rounds 40 -heap -heap_alloc
      
    2. Attendi il completamento dell'applicazione, quindi visualizza i dati del profilo per questa versione dell'applicazione:

      • Fai clic su ORA per caricare i dati del profilo più recenti.
      • Nel menu Versione, seleziona 3.
      • Nel menu Tipo di Profiler, seleziona Heap allocato.

      Ad esempio, il grafico a fiamma è il seguente:

      Grafico a fiamme dei profili heap allocati per la versione 3.

    Identificare il tasso di allocazione heap

    Il frame radice mostra la quantità totale di heap allocata durante i 10 secondi in cui è stato raccolto un profilo, calcolata come media di tutti i profili. In questo esempio, il frame principale mostra che, in media, sono stati allocati 1,535 GiB di memoria.

    Modificare l'applicazione

    Valutare la modifica

    Per valutare la modifica:

    1. Esegui l'applicazione con la versione dell'applicazione impostata su 4:

      go run . -version 4 -num_rounds 60 -heap -heap_alloc
      
    2. Attendi il completamento dell'applicazione, quindi visualizza i dati del profilo per questa versione dell'applicazione:

      • Fai clic su ORA per caricare i dati del profilo più recenti.
      • Nel menu Versione, seleziona 4.
      • Nel menu Tipo di Profiler, seleziona Heap allocato.
    3. Per quantificare l'effetto della modifica di readFiles sulla velocità di allocazione heap;heap, confronta i profili dell'heap allocato per la versione 4 con quelli raccolti per la versione 3:

      Confronto dei profili heap allocati tra le versioni 4 e 3.

      La descrizione comando del frame principale mostra che con la versione 4 la quantità media di memoria allocata durante la raccolta dei profili è diminuita di 1,301 GiB rispetto alla versione 3. La descrizione comando per readFiles.func1 mostra una diminuzione di 1,045 GiB:

      Confronto della descrizione comando di readfiles per il tipo di profilo heap allocato.

    4. Per quantificare l'effetto sulla garbage collection, configura un confronto dei profili di tempo della CPU. Nello screenshot seguente, viene applicato un filtro per mostrare gli stack per il garbage collector Go runtime.gcBgMarkWorker.*. Lo screenshot mostra che l'utilizzo della CPU per la garbage collection è ridotto dal 16,8% al 4,97%.

      Confronto tra l'utilizzo del tempo di CPU del processo di garbage collection in background della versione 4 e della versione 3.

    5. Per determinare se la modifica influisce sul numero di richieste al secondo gestite dall'applicazione, visualizza l'output in Cloud Shell. In questo esempio, la versione 4 completa fino a 15 richieste al secondo, un valore notevolmente superiore alle 5,8 richieste al secondo della versione 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
      ...
      

      L'aumento delle query al secondo gestite dall'applicazione potrebbe essere dovuto a un minor tempo dedicato alla garbage collection.

    • Puoi comprendere meglio l'effetto della modifica a readFiles visualizzando i profili heap. Un confronto dei profili heap dalla versione 4 a quella della versione 3 mostra che l'utilizzo dell'heap è diminuito da 70,95 MiB a 18,47 MiB:

      Confronto dell'utilizzo dell'heap per la versione 4 rispetto alla versione 3.

    Riepilogo

    In questa guida rapida sono stati utilizzati i profili del tempo di CPU e dell'heap allocato per identificare potenziali ottimizzazioni di un'applicazione. Gli obiettivi erano massimizzare il numero di richieste al secondo ed eliminare le allocazioni non necessarie.

    • Utilizzando i profili del tempo CPU, è stata identificata una funzione che utilizza molta CPU. Dopo aver applicato una semplice modifica, la tasso di richieste del server è aumentata a 5,8 al secondo, rispetto a circa 1 al secondo.

    • Utilizzando i profili heap allocati, la funzione shakesapp/server.go readFiles è stata identificata come avente un tasso di allocazione elevato. Dopo l'ottimizzazione di readFiles, la tasso di richieste del server è aumentata a 15 richieste al secondo e la quantità media di memoria allocata durante la raccolta del profilo di 10 secondi è diminuita di 1,301 GiB.

    Passaggi successivi

    Per informazioni sull'esecuzione dell'agente Cloud Profiler, consulta: