Estudo de caso: Isolar um problema de desempenho (C#, Visual Basic, F#)

Use as ferramentas de criação de perfil para investigar problemas de desempenho e isolar áreas problemáticas. Este estudo de caso usa um aplicativo de exemplo com problemas de desempenho para demonstrar como usar ferramentas de criação de perfil para melhorar a eficiência. Se você quiser comparar as ferramentas de criação de perfil, consulte Qual ferramenta devo escolher?

Este estudo de caso abrange os seguintes tópicos:

  • Como utilizar as ferramentas de criação de perfil do Visual Studio para analisar o desempenho do aplicativo.
  • Como interpretar os dados fornecidos por essas ferramentas para identificar gargalos de desempenho.
  • Como aplicar estratégias práticas para otimizar o código, com foco em Contadores .NET, contagens de chamadas e dados de tempo.

Acompanhe e aplique essas técnicas aos seus próprios aplicativos para torná-los mais eficientes e econômicos.

Estudo de caso Isolar um problema de desempenho

O aplicativo de exemplo neste estudo de caso é um aplicativo ASP.NET que executa consultas em um banco de dados simulado. O exemplo tem como base a Amostra de Diagnóstico.

O principal problema de desempenho com o aplicativo de exemplo está em padrões de codificação ineficientes. O aplicativo tem um gargalo de desempenho que afeta significativamente sua eficiência. O problema inclui os sintomas a seguir:

  • Baixo Uso da CPU: o aplicativo mostra baixo uso da CPU, indicando que a CPU não é o gargalo.

  • Alta Contagem de Threads do ThreadPool: a contagem de threads é relativamente alta e está aumentando constantemente, sugerindo privação do pool de threads.

  • Resposta Lenta do Aplicativo: o aplicativo responde lentamente devido à falta de threads disponíveis para processar novos itens de trabalho.

O estudo de caso visa abordar esses problemas utilizando as ferramentas de criação de perfil do Visual Studio para analisar o desempenho do aplicativo. Ao entender onde e como o desempenho do aplicativo pode ser melhorado, os desenvolvedores podem implementar otimizações para tornar o código mais rápido e eficiente. O objetivo final é melhorar o desempenho geral do aplicativo, tornando-o mais eficiente e econômico para execução.

Desafio

Há vários desafios para resolver os problemas de desempenho no aplicativo .NET de exemplo. Esses desafios decorrem da complexidade do diagnóstico de gargalos de desempenho. Os principais desafios na correção dos problemas descritos são:

  • Diagnosticar gargalos de desempenho: um dos principais desafios é identificar com precisão as causas básicas dos problemas de desempenho. O baixo uso da CPU combinado com o desempenho lento pode ter vários fatores contribuintes. Os desenvolvedores devem usar ferramentas de criação de perfil de forma eficaz para diagnosticar esses problemas, o que requer alguma compreensão de como essas ferramentas funcionam e como interpretar sua saída.

  • Conhecimento e Restrições de Recursos: as equipes podem enfrentar restrições relacionadas ao conhecimento, experiência e recursos. Criar o perfil e otimizar um aplicativo requer habilidades e experiência específicas, e nem todas as equipes podem ter acesso imediato a esses recursos.

Para enfrentar esses desafios, é necessária uma abordagem estratégica que combine o uso eficaz de ferramentas de criação de perfil, conhecimento técnico e planejamento e testes cuidadosos. O estudo de caso tem como objetivo orientar os desenvolvedores nesse processo, fornecendo estratégias e insights para superar esses desafios e melhorar o desempenho do aplicativo.

Estratégia

Aqui está uma visão de alto nível da abordagem neste estudo de caso:

  • Iniciamos a investigação observando as métricas do contador do .NET durante a coleta de dados de desempenho. Assim como a ferramenta Uso da CPU, a ferramenta Contadores do .NET do Visual Studio também é um bom ponto de partida para uma investigação de desempenho.
  • Em seguida, para obter informações adicionais e ajudar a isolar problemas ou melhorar o desempenho, considere coletar um rastreamento usando uma das outras ferramentas de criação de perfil. Por exemplo, dê uma olhada nas contagens de chamadas e nos dados de tempo usando a ferramenta Instrumentação.

A coleta de dados requer as seguintes tarefas:

  • Configurando o aplicativo para uma compilação de versão.
  • Selecione a ferramenta Contadores do .NET no Criador de Perfil de Desempenho (Alt+F2). (As etapas posteriores envolvem a ferramenta Instrumentação.)
  • No Criador de Perfil de Desempenho, inicie o aplicativo e colete um rastreamento.

Verificar contadores de desempenho

Ao executar o aplicativo, observamos os contadores na ferramenta Contadores do .NET. Para investigações iniciais, algumas métricas-chave para ficar de olho incluem:

  • CPU Usage. Observe esse contador para ver se há algum problema de desempenho com uso baixo ou elevado da CPU. Isso pode ser uma pista quanto a tipos específicos de problemas de desempenho. Por exemplo:
    • Com alto uso da CPU, use a ferramenta Uso da CPU para identificar áreas em que podemos otimizar o código. Para obter um tutorial sobre isso, consulte Estudo de caso: Guia para iniciantes sobre como otimizar o código.
    • Se houver uso baixo da CPU, use a ferramenta Instrumentação para identificar contagens de chamadas e tempo médio de função com base no tempo do relógio de parede. Isso pode ajudar a identificar problemas como contenção ou privação do pool de threads.
  • Allocation Rate. Para um aplicativo Web que atenda a solicitações, a taxa deve ser razoavelmente estável.
  • GC Heap Size. Observe esse contador para ver se o uso de memória está aumentando continuamente e potencialmente vazando. Se parecer alto, use uma das ferramentas de uso de memória.
  • Threadpool Thread Count. Para um aplicativo Web que atenda a solicitações, observe esse contador para ver se a contagem de threads está se mantendo estável ou aumentando a uma taxa constante.

Aqui está um exemplo mostrando como CPU Usage está baixo, enquanto ThreadPool Thread Count está relativamente alto.

Captura de tela dos contadores exibidos na ferramenta Contadores do .NET.

Uma contagem de threads cada vez maior com um baixo uso da CPU pode ser um indicador de insuficiência do pool de threads. O pool de threads é forçado a continuar iniciando novos threads. A insuficiência do pool de threads ocorre quando o pool não tem threads disponíveis para processar novos itens de trabalho e costuma causar lentidão na resposta dos aplicativos.

Com base no baixo uso da CPU e na contagem relativamente alta de threads, e trabalhando com a hipótese de um possível caso de insuficiência do pool de threads, mude para a ferramenta Instrumentação.

Investigar contagens de chamadas e dados de tempo

Vamos dar uma olhada em um rastreamento da ferramenta Instrumentação para ver se conseguimos descobrir mais sobre o que está acontecendo com os threads.

Depois de coletar um rastreamento com a ferramenta Instrumentação e carregá-lo no Visual Studio, primeiro verificamos a página de relatório inicial .diagsession que mostra os dados resumidos. No rastreamento coletado, usamos o link Abrir detalhes no relatório e selecionamos Flame Graph.

Captura de tela do Flame Graph na ferramenta Instrumentação.

A visualização do Flame Graph nos mostra que a função QueryCustomerDB (mostrada em amarelo) é responsável por uma parte significativa do tempo de execução do aplicativo.

Clique com o botão direito do mouse na função QueryCustomerDB e escolha Exibir na árvore de chamadas.

Captura de tela da Árvore de chamadas na ferramenta Instrumentação.

O caminho do código com maior uso de CPU no aplicativo é chamado de caminho frequente. O ícone de chama de caminho frequente (Captura de tela que mostra o ícone do Caminho Frequente.) pode ajudar a identificar rapidamente problemas de desempenho que podem ser melhorados.

Na exibição Árvore de Chamadas, você pode ver que o caminho frequente inclui a função QueryCustomerDB, o que aponta para um possível problema de desempenho.

Em relação ao tempo gasto em outras funções, os valores Próprio e Média próprio para a função QueryCustomerDB estão muito altos. Ao contrário de Total e Média total, os valores de Próprio excluem o tempo gasto em outras funções, portanto, esse é um bom lugar para procurar o gargalo de desempenho.

Dica

Se os valores de Próprio estivessem relativamente baixos em vez de altos, você provavelmente examinaria as consultas efetivas chamadas pela função QueryCustomerDB.

Clique duas vezes na função QueryCustomerDB para mostrar o código-fonte da função.

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

Fazemos uma pequena pesquisa. Como alternativa, podemos economizar tempo e deixar o Copilot fazer a pesquisa para nós.

Se estivermos usando o Copilot, selecionamos Perguntar ao Copilot no menu de contexto e digitamos a seguinte pergunta:

Can you identify a performance issue in the QueryCustomerDB method?

Dica

Você pode usar comandos de barra, como /optimize, para ajudar a formular boas perguntas para o Copilot.

O Copilot nos diz que esse código está chamando uma API assíncrona sem usar await. Esse é o padrão de código síncrono sobre assíncrono, que é uma causa comum de insuficiência do pool de threads e pode bloquear threads.

Para solucionar, use await. Neste exemplo, o Copilot fornece a sugestão de código a seguir junto com a explicação.

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

Se localizar problemas de desempenho relacionados a consultas de banco de dados, você poderá usar a ferramenta Banco de Dados para investigar se determinadas chamadas são mais lentas. Esses dados podem indicar uma oportunidade de otimizar consultas. Para obter um tutorial que mostra como usar a ferramenta Banco de Dados para investigar um problema de desempenho, consulte Estudo de caso: Guia para iniciantes sobre como otimizar o código. A ferramenta Banco de Dados é compatível com .NET Core com ADO.NET ou Entity Framework Core.

Para obter visualizações no Visual Studio para comportamento individual de thread, você pode usar a janela Pilhas paralelas durante a depuração. Essa janela mostra threads individuais juntamente com informações sobre threads que estão aguardando, as threads que elas estão aguardando e deadlocks.

Para obter mais informações sobre a insuficiência do pool de threads, consulte Detectando a insuficiência do pool de threads.

Próximas etapas

Os artigos e as postagens no blog apresentados a seguir fornecem mais informações para ajudar você a aprender a usar as ferramentas de desempenho do Visual Studio de maneira efetiva.