Perfilamento de código C#: encontrando gargalos na prática
\nA otimização de desempenho é uma das principais tarefas do desenvolvedor. Mesmo o código mais bonito pode ser lento se houver gargalos. Neste artigo, vamos abordar como perfilar código C# no ambiente .NET, encontrar trechos problemáticos e corrigi-los. Você conhecerá ferramentas práticas como BenchmarkDotNet, dotMemory e o Stopwatch integrado, além de obter exemplos prontos para análise.
Por que o perfilamento é importante?
\nPerfilamento não é apenas executar o aplicativo e medir o tempo. É uma abordagem sistemática para identificar "gargalos": algoritmos lentos, vazamentos de memória, consultas ineficientes ao banco de dados ou alocações frequentes no heap. Sem perfilamento, você corre o risco de gastar horas otimizando trechos que já são rápidos, ignorando os problemas reais.
\n\n1. Perfilamento com Stopwatch (início rápido)
\nA maneira mais simples de medir o tempo de execução do código é usar a classe System.Diagnostics.Stopwatch. Ela é adequada para uma avaliação inicial, mas não para benchmarks precisos devido à influência da compilação JIT e processos em segundo plano.
Exemplo de uso do Stopwatch
\nusing System.Diagnostics;\n\npublic class Example\n{\n public void Run()\n {\n Stopwatch sw = Stopwatch.StartNew();\n\n // Código a ser medido\n SimulateWork();\n\n sw.Stop();\n Console.WriteLine($"Tempo de execução: {sw.ElapsedMilliseconds} ms");\n }\n\n private void SimulateWork()\n {\n for (int i = 0; i < 1_000_000; i++)\n {\n // Simulação de trabalho\n }\n }\n}\nDesvantagens: O Stopwatch não considera o aquecimento JIT, coleta de lixo e outros fatores. Para análises sérias, use bibliotecas especializadas.
\n\n2. Perfilamento profissional com BenchmarkDotNet
\nBenchmarkDotNet é o padrão ouro para benchmarks no .NET. Ele aquece o código automaticamente, executa várias iterações, analisa estatísticas e considera o impacto do coletor de lixo. Instale o pacote via NuGet:
dotnet add package BenchmarkDotNet\n\nExemplo de benchmark para comparar dois métodos
\nusing BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Running;\n\npublic class StringBenchmark\n{\n private string[] data = { "apple", "banana", "cherry", "date" };\n\n [Benchmark]\n public string ConcatWithPlus()\n {\n string result = "";\n foreach (var s in data)\n result += s;\n return result;\n }\n\n [Benchmark]\n public string ConcatWithStringBuilder()\n {\n var sb = new System.Text.StringBuilder();\n foreach (var s in data)\n sb.Append(s);\n return sb.ToString();\n }\n}\n\npublic class Program\n{\n public static void Main()\n {\n var summary = BenchmarkRunner.Run<StringBenchmark>();\n }\n}\nApós a execução, você verá uma tabela com tempo, alocações de memória e erros. Isso permitirá comparar objetivamente StringBuilder e concatenação com +.
3. Análise de memória com dotMemory e Visual Studio Diagnostic Tools
\nGargalos não ocorrem apenas em velocidade, mas também no consumo de memória. A ferramenta dotMemory da JetBrains e os recursos integrados do Visual Studio (Diagnostic Tools) ajudam a encontrar vazamentos, alocações frequentes e objetos que vivem muito tempo no heap.
Como usar o Visual Studio Diagnostic Tools
\n- \n
- Execute o aplicativo no modo Debug. \n
- Abra a janela Diagnostic Tools (Debug → Windows → Show Diagnostic Tools). \n
- Ative o perfilamento de memória (Memory Usage) ou CPU (CPU Usage). \n
- Faça um snapshot de memória (Snapshot) antes e depois da execução do código.