Tutorial: como 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. Faça login na sua conta do Google.

    Se você ainda não tiver uma, inscreva-se.

  2. No Console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar a página do seletor de projetos

  3. Para ativar a API Cloud Profiler para seu projeto, no painel de navegação do Console do Google Cloud, clique em Profiler ou use o seguinte botão:

    Acessar o Profiler

  4. 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: