Otimizar um aplicativo Go

Neste guia de início rápido, você implanta um aplicativo Go intencionalmente ineficiente configurado para coletar dados de perfis. Você usa a interface do Profiler para visualizar os dados do perfil e identificar possíveis otimizações. Em seguida, você modifica o aplicativo, o implanta e avalia o efeito da modificação.

Antes de começar

  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. Para abrir o Cloud Shell, na barra de ferramentas do Console do Google Cloud, clique em Ativar o Cloud Shell:

    Ative o Cloud Shell.

    Após alguns instantes, uma sessão do Cloud Shell é aberta no Console do Google Cloud:

    Sessão do Cloud Shell.

Exemplo de aplicativo

O objetivo principal é maximizar o número de consultas por segundo que o servidor pode processar. O objetivo secundário é reduzir o uso da memória ao eliminar alocações de memória desnecessárias.

O servidor, usando um framework gRPC, recebe uma palavra ou frase e retorna o número de vezes que a palavra ou frase aparece nas obras de Shakespeare.

O número médio de consultas por segundo que o servidor pode processar é determinado pelo teste de carga do servidor. Para cada rodada de testes, um simulador de cliente é chamado e instruído a emitir 20 consultas sequenciais. Na conclusão de uma rodada, são exibidos o número de consultas enviadas pelo simulador de cliente, o tempo decorrido e o número médio de consultas por segundo.

O código do servidor é intencionalmente ineficiente.

Como executar o aplicativo de amostra

Faça o download e execute o aplicativo de amostra:

  1. No Cloud Shell, execute os seguintes comandos:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    cd golang-samples/profiler/shakesapp
    
  2. Execute o aplicativo com a versão definida como 1 e o número de rodadas definido como 15:

    go run . -version 1 -num_rounds 15
    

    Depois de um ou dois minutos, os dados do perfil são exibidos. Os dados do perfil são semelhantes ao exemplo a seguir:

    Gráfico de chama inicial quanto ao uso do tempo de CPU.

    Na captura de tela, observe que o Tipo de perfil está definido como CPU time. Isso indica que os dados de uso da CPU são exibidos no gráfico em degradê.

    O resultado da amostra exibido no Cloud Shell é mostrado abaixo:

    $ 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
    

    A resposta do Cloud Shell exibe o tempo decorrido de cada iteração e a taxa média de solicitações. Quando o aplicativo é iniciado, a entrada "Simulou 20 solicitações em 17,3s, taxa de 1,156069 solicitações / s" indica que o servidor está executando cerca de uma solicitação por segundo. Na última rodada, a entrada "Simulou 20 solicitações em 1m48.03s, taxa de 0,185134 solicitações / s" indica que o servidor está executando cerca de uma solicitação a cada cinco segundos.

Como usar perfis de tempo de CPU para maximizar consultas por segundo

Uma abordagem para maximizar o número de consultas por segundo é identificar métodos com uso intensivo da CPU e otimizar suas implementações. Nesta seção, você usa perfis de tempo de CPU para identificar um método que consome muita CPU no servidor.

Como identificar o uso do tempo de CPU

O frame raiz do gráfico em degradê lista o tempo total de CPU usado pelo aplicativo durante o intervalo de coleta de 10 segundos:

Visualização expandida do frame principal do gráfico de chama.

Neste exemplo, o serviço usou 2.37 s. Quando o sistema é executado em um único núcleo, o uso de tempo de CPU de 2,37 segundos corresponde a 23,7% de utilização desse núcleo. Saiba mais em Tipos de criação de perfil disponíveis.

Como modificar o aplicativo

Como avaliar a alteração

Para avaliar a alteração, faça o seguinte:

  1. Execute o aplicativo com a versão definida como 2:

    go run . -version 2 -num_rounds 40
    

    Uma seção posterior mostra que, com a otimização, o tempo necessário para executar uma única rodada é muito menor do que o do aplicativo não modificado. Para garantir a execução do aplicativo por um tempo suficiente para coletar e fazer o upload de perfis, o número de rodadas será maior.

  2. Aguarde a conclusão do aplicativo e, em seguida, visualize os dados do perfil para essa versão do aplicativo:

    • Clique em AGORA para carregar os dados mais recentes do perfil. Saiba mais em Intervalo de tempo.
    • No menu Versão, selecione 2.

Por exemplo, o gráfico de chama é:

Gráfico em degradê mostrando o uso da versão 2 da CPU

Nessa figura, o frame principal mostra um valor de 7.8 s. Como resultado da alteração da função de correspondência de strings, o tempo de CPU usado pelo aplicativo aumentou de 2,37 segundos para 7,8 segundos, ou, o aplicativo passou do uso de 23,7% para 78% de um núcleo da CPU.

A largura do frame é uma medida proporcional do uso do tempo de CPU. Neste exemplo, a largura do frame para GetMatchCount indica que a função usa cerca de 49% de todo o tempo de CPU usado pelo aplicativo. No gráfico de chama original, esse mesmo frame tinha cerca de 72% da largura do gráfico. Para ver o uso exato de tempo de CPU, use a dica do frame ou a lista de funções de foco:

Focar a lista de funções mostrando o uso da versão 2 do tempo de CPU.

A saída no Cloud Shell mostra que a versão modificada está concluindo cerca de 5,8 solicitações por segundo:

$ 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

A pequena alteração no aplicativo teve dois efeitos diferentes:

  • O número de solicitações por segundo aumentou de menos de 1 por segundo para 5,8 por segundo.

  • O tempo de CPU por solicitação, calculado pela divisão do uso da CPU pelo número de solicitações por segundo, foi reduzido de 13,4% para 23,7%.

    Observe que o tempo de CPU por solicitação diminuiu, embora seu uso tenha aumentado por 2,37 segundos, que corresponde a 23,7% de utilização de um único núcleo de CPU, para 7,8 segundos, 78% de um núcleo de CPU.

Como usar perfis de heap alocada para melhorar o uso de recursos

Esta seção ilustra como você pode usar o heap e os perfis de heap alocados para identificar um método de uso intensivo de alocação no aplicativo:

  • Os perfis de pilha mostram a quantidade de memória alocada no heap do programa no momento em que o perfil é coletado.

  • Os perfis de heap alocados mostram a quantidade total de memória que foi alocada no heap do programa durante o intervalo em que o perfil foi coletado. Ao dividir esses valores por 10 segundos, o intervalo de coleta de perfis, é possível interpretá-los como taxas de alocação.

Como ativar a coleta de perfis de heap

  1. Execute o aplicativo com a versão do aplicativo definida como 3 e ative a coleção de perfis de heap e de heap alocados.

    go run . -version 3 -num_rounds 40 -heap -heap_alloc
    
  2. Aguarde a conclusão do aplicativo e, em seguida, visualize os dados do perfil para essa versão do aplicativo:

    • Clique em AGORA para carregar os dados mais recentes do perfil.
    • No menu Versão, selecione 3.
    • No menu Tipo de criador de perfil, selecione Heap alocada.

    Por exemplo, o gráfico de chama é:

    Gráfico em degradê dos perfis de heap alocados para a versão 3.

Como identificar a taxa de alocação de heap

O frame raiz exibe a quantidade total de heap que foi alocada durante os 10 segundos em que um perfil foi coletado, na média de todos os perfis. Nesse exemplo, o frame raiz mostra que, em média, 1,535 GiB de memória foi alocado.

Como modificar o aplicativo

Como avaliar a alteração

Para avaliar a alteração, faça o seguinte:

  1. Execute o aplicativo com a versão definida como 4:

    go run . -version 4 -num_rounds 60 -heap -heap_alloc
    
  2. Aguarde a conclusão do aplicativo e, em seguida, visualize os dados do perfil para essa versão do aplicativo:

    • Clique em AGORA para carregar os dados mais recentes do perfil.
    • No menu Versão, selecione 4.
    • No menu Tipo de criador de perfil, selecione Heap alocada.
  3. Para quantificar o efeito da alteração de readFiles na taxa de alocação de heap, compare os perfis de heap alocados da versão 4 com os coletados para 3:

    Comparação dos perfis de heap alocados entre as versões 4 e 3.

    A dica do frame raiz mostra que, com a versão 4, a quantidade média de memória alocada durante a coleta do perfil diminuiu em 1,301 GiB em comparação com a versão 3. A dica de readFiles.func1 mostra uma diminuição de 1,045 GiB:

    Comparação da dica do readfiles para o tipo de perfil de heap alocado.

  4. Para quantificar o efeito na coleta de lixo, configure uma comparação de perfis de tempo de CPU. Na captura de tela a seguir, um filtro é aplicado para mostrar as pilhas do coletor de lixo do Go runtime.gcBgMarkWorker.*. A captura de tela mostra que o uso da CPU para coleta de lixo é reduzido para 4,97% de 16,8%.

    Comparação do uso do tempo de CPU do processo de coleta de lixo em segundo plano da v4 com a v3.

  5. Para determinar se há um impacto da alteração no número de solicitações por segundo manipuladas pelo aplicativo, veja a saída no Cloud Shell. Neste exemplo, a versão 4 conclui até 15 solicitações por segundo, significativamente mais que as 5,8 solicitações por segundo da versão 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
    ...
    

    O aumento nas consultas por segundo exibido pelo aplicativo pode ocorrer devido à redução do tempo na coleta de lixo.

  • Você pode entender melhor o efeito da modificação para readFiles visualizando os perfis de heap. Uma comparação dos perfis da heap para a versão 4 com a versão 3 mostra que o uso de heap diminuiu para 18,47 MiB de 70,95 MiB:

    Comparação do uso de heap da versão 4 com a versão 3.

Resumo

Neste guia de início rápido, os tempos de CPU e de heap alocados foram usados para identificar possíveis otimizações para um aplicativo. Os objetivos eram: maximizar o número de solicitações por segundo e eliminar alocações desnecessárias.

  • Ao usar perfis de tempo de CPU, uma função de uso intensivo da CPU foi identificada. Depois de aplicar uma alteração simples, a taxa de solicitação do servidor aumentou para 5,8 por segundo, de aproximadamente 1 por segundo.

  • Ao usar perfis de heap alocados, a função shakesapp/server.go readFiles foi identificada como tendo uma alta taxa de alocação. Depois de otimizar readFiles, a taxa de solicitação do servidor aumentou para 15 solicitações por segundo e a quantidade média de memória alocada durante a coleta de 10 segundos de perfil diminuiu 1,301 GiB.

A seguir

Saiba mais sobre como executar o agente do Cloud Profiler em: