Benchmarking com BenchmarkDotNet
Escreva microbenchmarks precisos com BenchmarkDotNet, analise vazão e alocações e evite armadilhas comuns.
Benchmarking com BenchmarkDotNet é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.
Por que usar o BenchmarkDotNet
A avaliação de desempenho em pequena escala é surpreendentemente difícil: o aquecimento do JIT, as pausas da coleta de lixo, os caches da CPU e o agendamento do sistema operacional introduzem ruído. O BenchmarkDotNet cuida de tudo isso automaticamente e produz resultados estatisticamente confiáveis.
Instalação e primeira avaliação de desempenho
Adicione o pacote NuGet, crie uma classe com métodos [Benchmark] e execute com BenchmarkRunner.Run. Sempre execute as avaliações de desempenho no modo Release.
// dotnet add package BenchmarkDotNet
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
[MemoryDiagnoser] // report allocations
[RankColumn] // rank methods by speed
public class StringBenchmarks
{
[Benchmark(Baseline = true)]
public string Concatenation()
{
var s = "";
for (int i = 0; i < 100; i++) s += i;
return s;
}
[Benchmark]
public string StringBuilder()
{
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 100; i++) sb.Append(i);
return sb.ToString();
}
}
// Program.cs (must run as Release):
BenchmarkRunner.Run<StringBenchmarks>();Lendo a tabela de resultados
O BenchmarkDotNet imprime uma tabela de resultados com tempo médio, erro, desvio padrão, proporção e alocação de memória. Veja como interpretar cada coluna.
// Example output:
// | Method | Mean | Error | StdDev | Ratio | Alloc |
// |-------------- |---------:|--------:|--------:|------:|------:|
// | Concatenation | 4,210 ns | 81 ns | 76 ns | 1.00 | 6.4KB |
// | StringBuilder | 312 ns | 4 ns | 4 ns | 0.07 | 1.6KB |
// Mean: average time per operation
// Error: half of 99.9% confidence interval
// StdDev: standard deviation — high value = noisy environment
// Ratio: relative to Baseline=true method (1.00)
// Alloc: managed heap allocated per operation
// Rule of thumb:
// Error should be < 5% of Mean for reliable results
// Run on an isolated machine (no background tasks)Avaliações de desempenho parametrizadas
Use [Params] para executar uma avaliação de desempenho com vários tamanhos de entrada. O BenchmarkDotNet executa todas as combinações e gera uma linha separada para cada valor de parâmetro.
[MemoryDiagnoser]
public class SearchBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N;
private int[] _data = Array.Empty<int>();
[GlobalSetup]
public void Setup()
=> _data = Enumerable.Range(0, N).ToArray();
[Benchmark]
public bool LinearSearch()
=> Array.IndexOf(_data, N - 1) >= 0;
[Benchmark]
public bool BinarySearch()
=> Array.BinarySearch(_data, N - 1) >= 0;
}Configuração e limpeza
Use atributos de ciclo de vida para executar uma configuração única ou uma configuração e limpeza a cada iteração. Isso mantém o custo da configuração fora do tempo medido da avaliação de desempenho.
[MemoryDiagnoser]
public class DbQueryBenchmarks
{
private NpgsqlConnection _conn = null!;
private NpgsqlCommand _cmd = null!;
[GlobalSetup] // runs once before all benchmarks
public void Setup()
{
_conn = new NpgsqlConnection("...");
_conn.Open();
_cmd = new NpgsqlCommand("SELECT id FROM orders LIMIT 100", _conn);
}
[IterationSetup] // runs before EACH benchmark iteration
public void IterationSetup() { /* per-iteration prep */ }
[Benchmark]
public int QueryOrders() => (int)_cmd.ExecuteScalar()!;
[GlobalCleanup] // runs once after all benchmarks
public void Cleanup() { _cmd.Dispose(); _conn.Dispose(); }
}Diagnósticos de memória
[MemoryDiagnoser] informa as alocações na área de memória gerenciada por operação. Combine-o com as opções [GcForce] para controlar o comportamento da coleta de lixo durante as avaliações de desempenho.
[MemoryDiagnoser] // adds Alloc column to results
public class AllocationBenchmarks
{
[Benchmark]
public int[] AllocateArray() => new int[1000];
[Benchmark]
public Span<int> UseStackalloc()
{
Span<int> span = stackalloc int[1000];
return span; // WARN: for illustration; don't return stackalloc
}
[Benchmark]
public int[] ArrayPoolRent()
{
var arr = System.Buffers.ArrayPool<int>.Shared.Rent(1000);
System.Buffers.ArrayPool<int>.Shared.Return(arr);
return arr;
}
}Diagnósticos: ETW e desmontagem
O BenchmarkDotNet oferece suporte a vários diagnósticos. O DisassemblyDiagnoser mostra a saída real do código de montagem nativo, sendo perfeito para verificar as otimizações do JIT.
using BenchmarkDotNet.Diagnosers;
[DisassemblyDiagnoser( // show generated ASM
maxDepth: 2,
printSource: true,
exportGithubMarkdown: true)]
[MemoryDiagnoser]
public class VectorizationBenchmarks
{
private float[] _data = new float[1024];
[Benchmark]
public float SumScalar()
{
float sum = 0;
foreach (var v in _data) sum += v;
return sum;
}
[Benchmark]
public float SumVectorized()
=> System.Numerics.Vector<float>.Count > 1
? VectorSum(_data)
: SumScalar();
}Configurações de execução
As configurações de execução controlam o ambiente de execução da avaliação de desempenho — número de iterações de aquecimento, chamadas por iteração, versão do ambiente de execução do .NET e configurações do JIT. Use configurações predefinidas ou personalizadas.
using BenchmarkDotNet.Jobs;
// Compare JIT vs Native AOT performance:
[SimpleJob(RuntimeMoniker.Net90)] // .NET 9 JIT
[SimpleJob(RuntimeMoniker.NativeAot90)] // .NET 9 AOT
[MemoryDiagnoser]
public class RuntimeComparison
{
[Benchmark]
public string SerializeJson()
{
var obj = new { Name = "Alice", Age = 30 };
return System.Text.Json.JsonSerializer.Serialize(obj);
}
}
// Custom job for more warmup:
[Config(typeof(MyConfig))]
public class PreciseBenchmarks
{
private class MyConfig : ManualConfig
{
public MyConfig()
=> AddJob(Job.Default.WithWarmupCount(5).WithIterationCount(20));
}
}Exportando resultados
O BenchmarkDotNet exporta resultados em vários formatos — Markdown (para solicitações de mesclagem no GitHub), CSV (para planilhas), JSON e gráficos HTML. As opções de exportação são configuradas na configuração da execução.
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Exporters.Csv;
[Config(typeof(ExportConfig))]
public class MyBenchmarks { /* ... */ }
public class ExportConfig : ManualConfig
{
public ExportConfig()
{
AddExporter(MarkdownExporter.GitHub);
AddExporter(CsvExporter.Default);
AddExporter(HtmlExporter.Default);
// Results in: BenchmarkDotNet.Artifacts/results/
}
}
// CLI usage to run specific benchmarks:
// dotnet run -c Release -- --filter "*StringBenchmarks*"
// dotnet run -c Release -- --list all (list all benchmarks)Caso real: otimização antes e depois
Usando o BenchmarkDotNet para validar uma otimização de desempenho — comparando abordagens ingênua, com pool e baseadas em Span.
[MemoryDiagnoser]
[RankColumn]
public class ParsingBenchmarks
{
private const string Input = "2025-06-15T08:30:00Z";
[Benchmark(Baseline = true)]
public DateTime ParseViaString()
=> DateTime.Parse(Input);
[Benchmark]
public DateTime ParseExact()
=> DateTime.ParseExact(Input,
"yyyy-MM-ddTHH:mm:ssZ",
System.Globalization.CultureInfo.InvariantCulture);
[Benchmark]
public bool TryParse()
=> DateTime.TryParseExact(Input.AsSpan(),
"yyyy-MM-ddTHH:mm:ssZ",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out _);
}Verificação rápida
Por que as avaliações de desempenho do BenchmarkDotNet devem ser executadas com dotnet run -c Release?
Recapitulação: avaliação de desempenho com BenchmarkDotNet
Principais conclusões:
- Instale via NuGet; marque os métodos com
[Benchmark]; execute comdotnet run -c Release [MemoryDiagnoser]: adiciona a coluna de alocação;[RankColumn]: classifica por velocidade[Params]: execute com vários tamanhos de entrada para descobrir a complexidade algorítmica[GlobalSetup]/[GlobalCleanup]: configuração única fora do tempo medido[DisassemblyDiagnoser]: inspecione o ASM nativo gerado para verificar as otimizações do JIT- Use configurações de execução para comparar o JIT do .NET 9 com Native AOT e com diferentes números de iterações
Perguntas Frequentes
A aula “Benchmarking com BenchmarkDotNet” é grátis?
Sim — o texto completo de “Benchmarking com BenchmarkDotNet” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.
O que vou aprender em “Benchmarking com BenchmarkDotNet”?
Escreva microbenchmarks precisos com BenchmarkDotNet, analise vazão e alocações e evite armadilhas comuns. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C# Academy?
Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Benchmarking com BenchmarkDotNet”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C# Academy?
Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Compilação Native AOT
- Limitações de corte e reflexão
- ReadyToRun e compilação em camadas
- Benchmarking com BenchmarkDotNet