BenchmarkDotNet и микробенчмарки

↑ 7.1 Производительность .NET и кэширование · ← Предыдущая · → Следующая

Желательно~3 мин чтенияУровень: senior

Зачем это на собесе

Как доказывать, что оптимизация работает; типичные ошибки бенчмарков.

Объяснение

BenchmarkDotNet запускает код в изолированном процессе, прогревает JIT, делает много итераций и считает статистику.

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
public class ConcatBench
{
    private readonly string[] _parts = Enumerable.Range(0, 100).Select(i => i.ToString()).ToArray();
 
    [Benchmark(Baseline = true)]
    public string Plus() { var s = ""; foreach (var p in _parts) s += p; return s; }
 
    [Benchmark]
    public string Builder() { var sb = new StringBuilder(); foreach (var p in _parts) sb.Append(p); return sb.ToString(); }
 
    [Benchmark]
    public string Join() => string.Concat(_parts);
}
 
BenchmarkRunner.Run<ConcatBench>();   // dotnet run -c Release

Результат: среднее время, ошибка, StdDev, отношение к baseline, Gen0/Gen1/Gen2 и Allocated.

Нюансы и подводные камни

  • Только Release без отладчика.
  • JIT может удалить код без побочных эффектов (dead code elimination): возвращайте результат.
  • Параметризуйте размеры ([Params]) — оптимизация может ломаться на других объёмах.
  • Микробенчмарк не заменяет измерение в реальной системе: сеть, БД и GC ведут себя иначе.
  • Стабильность: закройте фоновые процессы, не сравнивайте между машинами.
  • Тиеринг JIT (TieredPGO) влияет на результаты: используйте warmup, как делает BDN.

Практика

  1. Сравните string +, StringBuilder, string.Concat с MemoryDiagnoser.
  2. Сравните foreach по List<T> и по IEnumerable<T>.
  3. Встройте бенчмарк в отдельный проект и храните результаты в репозитории.

Вопросы с ответами

Связанные темы