Package | Version | Downloads | Samples |
---|---|---|---|
TinyBenchmark | |||
TinyBenchmark.Exporters |
Define benchmarks with ease!
TinyBenchmark provides a simple set of APIs that enable you to inspect the execution time of different methods and compare them between each other.
Usage
- Run your first benchmark
- Define benchmark arguments
- Average the results over multiple iterations
- Named benchmarks
- Benchmarks comparison
- Benchmarks output
Attributes
- [Benchmark]: identifies a benchmark method
- [Arguments]: defines the benchmark arguments
- [WarmupWith]: identifies a warmup method to be executed before the benchmark
- [Param]: defines all the possible values of a property that will be used by all the benchmarks
- [Init]: identifies an initialization method to be executed before each benchmark
- [InitWith]: identifies an initialization method to be executed before the benchmark
- [BenchmarksContainer]: defines additional information about the class that contains the benchmarks
- [InitContainer]: identifies an initialization method to be executed once before every benchmark
class Demo
{
public static void Main(string[] args)
{
var runner = new BenchmarkRunner();
var report = runner.Run<BenchmarksContainer>();
// Explore the data in the report!
Console.WriteLine($"Total duration: {report.Duration}");
}
}
class BenchmarksContainer
{
[Benchmark]
public void TestMe()
{
string token = "test";
string msg = string.Empty;
for (int i = 0; i < 10_000; i++)
msg += token;
}
}
class BenchmarksContainer
{
/// <summary>
/// This benchmark will be executed once for each [Arguments],
/// with the "times" variable assigned to the value specified the attribute.
/// </summary>
[Benchmark]
[Arguments(10_000)]
[Arguments(100_000)]
public void TestMe(int times)
{
string token = "test";
string msg = string.Empty;
for (int i = 0; i < times; i++)
msg += token;
}
}
class Demo
{
public static void Main(string[] args)
{
var runner = new BenchmarkRunner();
var report = runner.Run<BenchmarksContainer>();
var benchmarkReport = report.Reports.First();
Console.WriteLine($"Benchmark: {benchmarkReport.Name}");
Console.WriteLine($" average iteration duration: {benchmarkReport.AvgIterationDuration}");
}
}
class BenchmarksContainer
{
/// <summary>
/// This benchmark will be executed once for each [Arguments]
/// and once for each iteration, for a total of 6 runs.
/// </summary>
[Benchmark(Iterations = 3)]
[Arguments(10_000)]
[Arguments(100_000)]
public void TestMe(int times)
{
string token = "test";
string msg = string.Empty;
for (int i = 0; i < times; i++)
msg += token;
}
}
class BenchmarksContainer
{
[Benchmark(Name = "String concatenation")]
public void StringConcatenation()
{
string msg = string.Empty;
for (int i = 0; i < 50_000; i++)
msg += "test";
}
}
class Demo
{
public static void Main(string[] args)
{
var runner = new BenchmarkRunner();
var report = runner.Run<BenchmarksContainer>();
var stringConcatenationBenchmarkReport = report.Reports[0];
Console.WriteLine($"Benchmark: {stringConcatenationBenchmarkReport.Name}");
Console.WriteLine($" average iteration duration: {stringConcatenationBenchmarkReport.AvgIterationDuration}");
var stringBuilderBenchmarkReport = report.Reports[1];
Console.WriteLine($"Benchmark: {stringBuilderBenchmarkReport.Name}");
Console.WriteLine($" average iteration duration: {stringBuilderBenchmarkReport.AvgIterationDuration}");
var efficiency = Math.Round(stringConcatenationBenchmarkReport.AvgIterationDuration / stringBuilderBenchmarkReport.AvgIterationDuration, 1);
Console.WriteLine($"{stringBuilderBenchmarkReport.Name} is {efficiency} times faster than {stringConcatenationBenchmarkReport.Name}!");
}
}
public class BenchmarksContainer
{
private readonly string _token = "test";
private readonly int _tokensCount = 50_000;
[Benchmark(Name = "String concatenation")]
public void StringConcatenation()
{
string msg = string.Empty;
for (int i = 0; i < _tokensCount; i++)
msg += _token;
}
[Benchmark(Name = "Concatenation using StringBuilder")]
public void StringBuilder()
{
var sb = new StringBuilder();
for (int i = 0; i < _tokensCount; i++)
sb.Append(_token);
var msg = sb.ToString();
}
}
Each benchmark can write to the console using an instance of IBenchmarkOutput
.
Simply declare a constructor with this parameter and the library will inject an instance of it.
Compared to simply using Console.WriteLine
, this type has the advantage that is aware of the OutputLevel
associated with each message, and therefore the user can easily configure how many logs should be displayed on the console.
These are the current values of OutputLevel
:
Verbose
: print everything, including the benchmarks outputNormal
: print less information compared to Verbose, but still give enough details about the execution.Minimal
: print only the main steps of the executionErrorsOnly
: print only if some unexpected errors occurrSilent
: don't print "anything"
Unless specified otherwise, the default output level of a BenchmarkRunner
is Normal
(therefore, by default, the benchmarks' logs won't be shown).
class Demo
{
public static void Main(string[] args)
{
var runner = new BenchmarkRunner(maxOutputLevel: OutputLevel.Verbose);
var report = runner.Run<BenchmarksContainer>();
var benchmarkReport = report.Reports.First();
Console.WriteLine($"Benchmark: {benchmarkReport.Name}");
Console.WriteLine($" average iteration duration: {benchmarkReport.AvgIterationDuration}");
}
}
class BenchmarksContainer
{
private readonly IBenchmarkOutput _output;
public BenchmarksContainer(IBenchmarkOutput output)
{
_output = output;
}
[Benchmark(Name = "String concatenation")]
public void StringConcatenation()
{
int times = 50_000;
string token = "test";
_output.WriteLine($"Concatenating {times} times the string \"{token}\"");
string msg = string.Empty;
for (int i = 0; i < times; i++)
msg += token;
_output.WriteLine("Terminated");
}
}