Otimização de desempenho de aplicações C#: melhores práticas .NET

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course
Here is the translated HTML content in Brazilian Portuguese: ```html

Introdução: Por que a Performance em C# Importa

No mundo moderno do desenvolvimento de software, a velocidade da aplicação impacta diretamente a experiência do usuário, os custos de infraestrutura e a competitividade do produto. A plataforma .NET e a linguagem C# fornecem ferramentas poderosas para criar sistemas de alto desempenho, no entanto, o uso incorreto de recursos (por exemplo, gerenciamento de memória descontrolado ou algoritmos ineficientes) pode levar a atrasos sérios e consumo excessivo de recursos. Neste artigo, vamos detalhar técnicas-chave para otimizar a performance de aplicações C# que ajudarão você a escrever código mais rápido e eficiente.

1. Profiling: Encontre o Gargalo Antes de Otimizar

Antes de fazer alterações, é necessário determinar com precisão onde o maior tempo é gasto. Sem profiling, você corre o risco de otimizar o que já é rápido, perdendo os verdadeiros gargalos.

Ferramentas de Profiling

  • Ferramentas de Diagnóstico do Visual Studio — um profiler integrado para analisar CPU, memória e exceções.
  • dotMemory — uma ferramenta poderosa para analisar gerenciamento de memória e encontrar vazamentos.
  • PerfView — uma ferramenta gratuita da Microsoft para análise profunda de performance .NET.
  • BenchmarkDotNet — uma biblioteca para micro-benchmarking de seções individuais de código.

Exemplo de uso do BenchmarkDotNet para medir a velocidade de dois métodos:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class StringBenchmark
{
    private string[] data = Enumerable.Range(0, 1000).Select(i => $"Item {i}").ToArray();

    [Benchmark]
    public string ConcatenateWithPlus()
    {
        string result = "";
        foreach (var s in data) result += s;
        return result;
    }

    [Benchmark]
    public string ConcatenateWithStringBuilder()
    {
        var sb = new System.Text.StringBuilder();
        foreach (var s in data) sb.Append(s);
        return sb.ToString();
    }
}

// Run: BenchmarkRunner.Run<StringBenchmark>();

Os resultados do benchmark mostrarão claramente que StringBuilder é dezenas de vezes mais rápido que a concatenação usando + em loops.

2. Gerenciamento de Memória e Redução da Pressão no GC

O Garbage Collector (GC) é um mecanismo poderoso, mas sua operação frequente pode impactar negativamente a performance, especialmente em sistemas de alta carga. O objetivo principal é minimizar o número de alocações e o tempo de vida dos objetos.

2.1. Usando Structs em Vez de Classes

Se um objeto é pequeno (menos de 16-24 bytes) e tem um tempo de vida curto, considere usar um struct. Structs são armazenadas na pilha (ou embutidas no objeto pai) e não criam pressão no GC.

// Classe — alocação no heap
public class PointClass
{
    public int X { get; set; }
    public int Y { get; set; }
}

// Struct — na pilha
public struct PointStruct
{
    public int X;
    public int Y;
}

2.2. Object Pooling

Para objetos frequentemente criados e destruídos (por exemplo, conexões de banco de dados, buffers), use um pool. O .NET Core tem uma implementação embutida ObjectPool<T>.

using Microsoft.Extensions.ObjectPool;

var pool = new DefaultObjectPool<List<int>>(new ListPolicy());
var list = pool.Get();
try
{
    // Trabalhe com a lista
}
finally
{
    pool.Return(list); // Retorne ao pool
}

2.3. Span<T> e Memory<T>

Esses tipos permitem trabalhar com regiões contíguas de memória sem alocações adicionais. Eles são ideais para parsing, trabalho com arrays e strings.

public int SumDigits(ReadOnlySpan<                

Blogs

Book Recommendations