Single mode: Benchmark
Benchmark is the entry point for one-off measurements. It requires no class structure, no attributes, and no project setup beyond adding the NuGet reference. Use it anywhere you want a quick, reliable number.
Basic usage
using NBenchmark;
var result = Benchmark.Run(() =>
{
// code to measure
for (int i = 0; i < 1000; i++) { }
});Benchmark.Run warms up until the timings plateau, collects measured samples until the confidence interval is tight enough, trims outliers using the IQR fence rule, and returns a BenchmarkResult.
Overloads
Synchronous
// Action - for code with no return value
var result = Benchmark.Run(() => DoWork());
// Func<T> - returns a value so the runner can prevent dead-code elimination
var result = Benchmark.Run(() => ComputeHash(data));Async
// Func<Task>
var result = await Benchmark.RunAsync(async () => await FetchDataAsync());
// Func<Task<T>>
var result = await Benchmark.RunAsync(async () => await ComputeAsync(input));Raw outcome
Benchmark.RunRaw returns a MeasurementOutcome which includes both the BenchmarkResult and the raw per-iteration sample array. Use this if you need the underlying data.
var outcome = Benchmark.RunRaw(() => DoWork());
double[] rawSamples = outcome.RawSamples; // nanoseconds, before outlier trimming
BenchmarkResult result = outcome.Result;Custom options
Pass a MeasurementOptions instance to override the defaults:
var options = new MeasurementOptions
{
Iterations = 500,
WarmupIterations = 50,
MeasureAllocations = true,
ConfidenceLevel = 0.99,
};
var result = Benchmark.Run(() => MyMethod(), options: options);See Configuration for the full list of options.
Naming the benchmark
The name parameter sets the label used in output and file reporters:
var result = Benchmark.Run(() => MyMethod(), name: "MyMethod with 1000-item input");Displaying results
Plain text (core package)
result.Print();Output:
┌─ Benchmark ─────────────────────────────────────
│
│ Median: 342.1 ns Mean: 348.7 ns
│ Ops/s: 2.87 Mops/s Median ops/s: 2.92 Mops/s
│ P95: 361.2 ns P99: 378.5 ns P99.9: 380.0 ns
│ StdDev: 8.3 ns CV: 2.38%
│ Error: ±3.1 ns (0.89% of Mean)
│ CI: [345.6 ns … 351.8 ns] (95%)
│ Alloc/op: 0 B
│
└─────────────────────────────────────────────────Rich console table (NBenchmark.Reporters.Console)
using NBenchmark.Reporters.Console;
await result.PrintAsync();This runs the result through ConsoleReporter and renders a Spectre.Console table.
File reporters
await result.ToMarkdownAsync("results.md");
await result.ToCsvAsync("results.csv");
await result.ToJsonAsync("results/"); // output directoryAccessing result fields directly
BenchmarkResult is a plain record - access any field directly:
Console.WriteLine($"Median: {result.Median} ns");
Console.WriteLine($"Mean: {result.Mean} ns");
Console.WriteLine($"Ops/s: {result.OperationsPerSecond}");
Console.WriteLine($"P95: {result.GetPercentile(0.95)} ns");
Console.WriteLine($"StdDev: {result.StandardDeviation} ns");
Console.WriteLine($"Error: ±{result.MarginOfError} ns ({result.ConfidenceLevel * 100:0}% CI)");
Console.WriteLine($"CI: {result.ConfidenceIntervalLower} … {result.ConfidenceIntervalUpper} ns");
if (result.MeanAllocatedBytes.HasValue)
Console.WriteLine($"Alloc: {result.MeanAllocatedBytes.Value} bytes/op");What Benchmark does not do
- It does not compare benchmarks. Use BenchmarkSuite for A/B comparisons.
- It does not run significance testing between multiple results. Significance testing requires paired raw samples and is handled by
BenchmarkSuiteandBenchmarkHarness.
Next steps
- Suite mode: BenchmarkSuite - compare two or more implementations
- Configuration - full options reference
- Reporters - save results to files
