Parameterization
Sample: IntroParams
You can mark one or several fields or properties in your class by
the [Params]
attribute.
In this attribute, you can specify set of values.
Every value must be a compile-time constant.
As a result, you will get results for each combination of params values.
Source code
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroParams
{
[Params(100, 200)]
public int A { get; set; }
[Params(10, 20)]
public int B { get; set; }
[Benchmark]
public void Benchmark() => Thread.Sleep(A + B + 5);
}
}
Output
| Method | A | B | Mean | Error | StdDev |
|---------- |---- |--- |---------:|--------:|--------:|
| Benchmark | 100 | 10 | 115.3 ms | 0.13 ms | 0.12 ms |
| Benchmark | 100 | 20 | 125.4 ms | 0.14 ms | 0.12 ms |
| Benchmark | 200 | 10 | 215.5 ms | 0.19 ms | 0.18 ms |
| Benchmark | 200 | 20 | 225.4 ms | 0.17 ms | 0.16 ms |
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroParams
Sample: IntroParamsSource
In case you want to use a lot of values, you should use
[ParamsSource]
You can mark one or several fields or properties in your class by the
[Params]
attribute.
In this attribute, you have to specify the name of public method/property which is going to provide the values
(something that implements IEnumerable
).
The source must be within benchmarked type!
Source code
using System.Collections.Generic;
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroParamsSource
{
// property with public setter
[ParamsSource(nameof(ValuesForA))]
public int A { get; set; }
// public field
[ParamsSource(nameof(ValuesForB))]
public int B;
// public property
public IEnumerable<int> ValuesForA => new[] { 100, 200 };
// public static method
public static IEnumerable<int> ValuesForB() => new[] { 10, 20 };
[Benchmark]
public void Benchmark() => Thread.Sleep(A + B + 5);
}
}
Output
| Method | B | A | Mean | Error | StdDev |
|---------- |--- |---- |---------:|--------:|--------:|
| Benchmark | 10 | 100 | 115.5 ms | 0.17 ms | 0.16 ms |
| Benchmark | 10 | 200 | 215.6 ms | 0.15 ms | 0.14 ms |
| Benchmark | 20 | 100 | 125.5 ms | 0.19 ms | 0.18 ms |
| Benchmark | 20 | 200 | 225.5 ms | 0.23 ms | 0.22 ms |
Remarks
A remark about IParam.
You don't need to use IParam
anymore since 0.11.0
.
Just use complex types as you wish and override ToString
method to change the display names used in the results.
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroParamsSource
Sample: IntroParamsAllValues
If you want to use all possible values of an enum
or another type with a small number of values, you can use the [ParamsAllValues]
attribute, instead of listing all the values by hand. The types supported by the attribute are:
bool
- any
enum
that is not marked with[Flags]
Nullable<T>
, whereT
is an enum or boolean
Source code
using BenchmarkDotNet.Attributes;
using System.Threading;
namespace BenchmarkDotNet.Samples
{
[DryJob]
public class IntroParamsAllValues
{
public enum CustomEnum
{
One = 1,
Two,
Three
}
[ParamsAllValues]
public CustomEnum E { get; set; }
[ParamsAllValues]
public bool? B { get; set; }
[Benchmark]
public void Benchmark()
{
Thread.Sleep(
(int)E * 100 +
(B == true ? 20 : B == false ? 10 : 0));
}
}
}
Output
Method | E | B | Mean | Error |
---------- |------ |------ |---------:|------:|
Benchmark | One | ? | 101.4 ms | NA |
Benchmark | One | False | 111.1 ms | NA |
Benchmark | One | True | 122.0 ms | NA |
Benchmark | Two | ? | 201.3 ms | NA |
Benchmark | Two | False | 212.1 ms | NA |
Benchmark | Two | True | 221.3 ms | NA |
Benchmark | Three | ? | 301.4 ms | NA |
Benchmark | Three | False | 311.5 ms | NA |
Benchmark | Three | True | 320.8 ms | NA |
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroParamsAllValues
Sample: IntroParamsPriority
In order to sort columns of parameters in the results table you can use the Property Priority
inside the params attribute. The priority range is [Int32.MinValue;Int32.MaxValue]
, lower priorities will appear earlier in the column order. The default priority is set to 0
.
Source code
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroParamsPriority
{
[Params(100)]
public int A { get; set; }
[Params(10, Priority = -100)]
public int B { get; set; }
[Benchmark]
public void Benchmark() => Thread.Sleep(A + B + 5);
}
}
Output
| Method | B | A | Mean | Error | StdDev |
|---------- |--- |---- |---------:|--------:|--------:|
| Benchmark | 10 | 100 | 115.4 ms | 0.12 ms | 0.11 ms |
Links
- Priority BaseClass
PriorityAttribute.cs
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroParamsPriority
Sample: IntroArguments
As an alternative to using [Params]
,
you can specify arguments for your benchmarks.
There are several ways to do it (described below).
The [Arguments]
allows you to provide a set of values.
Every value must be a compile-time constant (it's C# language limitation for attributes in general).
You can also combine
[Arguments]
with
[Params]
.
As a result, you will get results for each combination of params values.
Source code
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroArguments
{
[Params(true, false)] // Arguments can be combined with Params
public bool AddExtra5Milliseconds;
[Benchmark]
[Arguments(100, 10)]
[Arguments(100, 20)]
[Arguments(200, 10)]
[Arguments(200, 20)]
public void Benchmark(int a, int b)
{
if (AddExtra5Milliseconds)
Thread.Sleep(a + b + 5);
else
Thread.Sleep(a + b);
}
}
}
Output
| Method | AddExtra5Miliseconds | a | b | Mean | Error | StdDev |
|---------- |--------------------- |---- |--- |---------:|----------:|----------:|
| Benchmark | False | 100 | 10 | 110.1 ms | 0.0056 ms | 0.0044 ms |
| Benchmark | False | 100 | 20 | 120.1 ms | 0.0155 ms | 0.0138 ms |
| Benchmark | False | 200 | 10 | 210.2 ms | 0.0187 ms | 0.0175 ms |
| Benchmark | False | 200 | 20 | 220.3 ms | 0.1055 ms | 0.0986 ms |
| Benchmark | True | 100 | 10 | 115.3 ms | 0.1375 ms | 0.1286 ms |
| Benchmark | True | 100 | 20 | 125.3 ms | 0.1212 ms | 0.1134 ms |
| Benchmark | True | 200 | 10 | 215.4 ms | 0.0779 ms | 0.0691 ms |
| Benchmark | True | 200 | 20 | 225.4 ms | 0.0775 ms | 0.0725 ms |
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroArguments
Sample: IntroArgumentsSource
In case you want to use a lot of values, you should use
[ArgumentsSource]
.
You can mark one or several fields or properties in your class by the
[ArgumentsSource]
attribute.
In this attribute, you have to specify the name of public method/property which is going to provide the values
(something that implements IEnumerable
).
The source must be within benchmarked type!
Source code
using System;
using System.Collections.Generic;
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroArgumentsSource
{
[Benchmark]
[ArgumentsSource(nameof(Numbers))]
public double ManyArguments(double x, double y) => Math.Pow(x, y);
public IEnumerable<object[]> Numbers() // for multiple arguments it's an IEnumerable of array of objects (object[])
{
yield return new object[] { 1.0, 1.0 };
yield return new object[] { 2.0, 2.0 };
yield return new object[] { 4.0, 4.0 };
yield return new object[] { 10.0, 10.0 };
}
[Benchmark]
[ArgumentsSource(nameof(TimeSpans))]
public void SingleArgument(TimeSpan time) => Thread.Sleep(time);
public IEnumerable<object> TimeSpans() // for single argument it's an IEnumerable of objects (object)
{
yield return TimeSpan.FromMilliseconds(10);
yield return TimeSpan.FromMilliseconds(100);
}
}
}
Output
| Method | x | y | Mean | Error | StdDev |
|------- |--- |--- |----------:|----------:|----------:|
| Pow | 1 | 1 | 9.360 ns | 0.0190 ns | 0.0149 ns |
| Pow | 2 | 2 | 40.624 ns | 0.3413 ns | 0.3192 ns |
| Pow | 4 | 4 | 40.537 ns | 0.0560 ns | 0.0524 ns |
| Pow | 10 | 10 | 40.395 ns | 0.3274 ns | 0.3063 ns |
Another example
If the values are complex types you need to override ToString
method to change the display names used in the results.
[DryJob]
public class WithNonPrimitiveArgumentsSource
{
[Benchmark]
[ArgumentsSource(nameof(NonPrimitive))]
public void Simple(SomeClass someClass, SomeStruct someStruct)
{
for (int i = 0; i < someStruct.RangeEnd; i++)
Console.WriteLine($"// array.Values[{i}] = {someClass.Values[i]}");
}
public IEnumerable<object[]> NonPrimitive()
{
yield return new object[] { new SomeClass(Enumerable.Range(0, 10).ToArray()), new SomeStruct(10) };
yield return new object[] { new SomeClass(Enumerable.Range(0, 15).ToArray()), new SomeStruct(15) };
}
public class SomeClass
{
public SomeClass(int[] initialValues) => Values = initialValues.Select(val => val * 2).ToArray();
public int[] Values { get; }
public override string ToString() => $"{Values.Length} items";
}
public struct SomeStruct
{
public SomeStruct(int rangeEnd) => RangeEnd = rangeEnd;
public int RangeEnd { get; }
public override string ToString() => $"{RangeEnd}";
}
}
| Method | someClass | someStruct | Mean | Error |
|------- |---------- |----------- |---------:|------:|
| Simple | 10 items | 10 | 887.2 us | NA |
| Simple | 15 items | 15 | 963.1 us | NA |
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroArgumentsSource
Sample: IntroArrayParam
Warning
The cost of creating the arguments is not included in the benchmark.
So if you want to pass an array as an argument, we are going to allocate it before running the benchmark, and the benchmark will not include this operation.
Source code
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroArrayParam
{
[Benchmark]
[ArgumentsSource(nameof(Data))]
public int ArrayIndexOf(int[] array, int value)
=> Array.IndexOf(array, value);
[Benchmark]
[ArgumentsSource(nameof(Data))]
public int ManualIndexOf(int[] array, int value)
{
for (int i = 0; i < array.Length; i++)
if (array[i] == value)
return i;
return -1;
}
public IEnumerable<object[]> Data()
{
yield return new object[] { new int[] { 1, 2, 3 }, 4 };
yield return new object[] { Enumerable.Range(0, 100).ToArray(), 4 };
yield return new object[] { Enumerable.Range(0, 100).ToArray(), 101 };
}
}
}
Output
| Method | array | value | Mean | Error | StdDev | Allocated |
|-------------- |----------- |------ |----------:|----------:|----------:|----------:|
| ArrayIndexOf | Array[100] | 4 | 15.558 ns | 0.0638 ns | 0.0597 ns | 0 B |
| ManualIndexOf | Array[100] | 4 | 5.345 ns | 0.0668 ns | 0.0625 ns | 0 B |
| ArrayIndexOf | Array[3] | 4 | 14.334 ns | 0.1758 ns | 0.1558 ns | 0 B |
| ManualIndexOf | Array[3] | 4 | 2.758 ns | 0.0905 ns | 0.1208 ns | 0 B |
| ArrayIndexOf | Array[100] | 101 | 78.359 ns | 1.8853 ns | 2.0955 ns | 0 B |
| ManualIndexOf | Array[100] | 101 | 80.421 ns | 0.6391 ns | 0.5978 ns | 0 B |
Links
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroArrayParam
Sample: IntroArgumentsPriority
Like Params also Argument columns can be sorted in the table result through their Priority
. The priority should be defined only once for multiple Arguments and will keep their inner order as they are defined in the method.
Source code
using System.Collections.Generic;
using System.Threading;
using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNet.Samples
{
public class IntroArgumentsPriority
{
[Params(100, Priority = 0)] // Argument priority can be combined with Params priority
public int A { get; set; }
[Arguments(5, Priority = -10)] // Define priority just once for multiple argument attributes
[Arguments(10)]
[Arguments(20)]
[Benchmark]
public void Benchmark(int b) => Thread.Sleep(A + b);
[Benchmark]
[ArgumentsSource(nameof(Numbers), Priority = 10)]
public void ManyArguments(int c, int d) => Thread.Sleep(A + c + d);
public IEnumerable<object[]> Numbers()
{
yield return new object[] { 1, 2 };
}
}
}
Output
| Method | b | A | c | d | Mean | Error | StdDev |
|-------------- |--- |---- |-- |-- |---------:|--------:|--------:|
| ManyArguments | ? | 100 | 1 | 2 | 103.4 ms | 0.09 ms | 0.08 ms |
| Benchmark | 5 | 100 | ? | ? | 105.5 ms | 0.21 ms | 0.19 ms |
| Benchmark | 10 | 100 | ? | ? | 110.5 ms | 0.14 ms | 0.14 ms |
| Benchmark | 20 | 100 | ? | ? | 120.4 ms | 0.16 ms | 0.15 ms |
Links
- Priority BaseClass
PriorityAttribute.cs
- Parameterization
- The permanent link to this sample: BenchmarkDotNet.Samples.IntroArgumentsPriority