BenchmarkDotNet es una biblioteca para la realización de test de rendimiento de nuestro código en .NET.
El rendimiento y velocidad de nuestro código es un parámetro importante que debemos tener en cuenta y que, en ocasiones, es el principal punto de interés. Para medir el rendimiento lo normal es que ejecutemos un test para comprobar su velocidad, o lo que llamamos benchmark.
Sin embargo, hacer un benchmark correctamente es más complicado de lo que normalmente se piensa, debido a que tanto el entorno de desarrollo como el compilador van a hacer todo tipo de cosas, que pueden interferir con la medición del resultado.
De hecho, si no usas una herramienta de algún tipo (y esta es la mejor que conozco para .NET) los resultados de tus pruebas de rendimiento no van a ser fiables.
Aquí es donde entra en juego BenchmarkDotNet, encargándose precisamente de esto. De ejecutar una serie de test de forma que los resultados sean comparables, repetibles, y precisos.
Otras de sus características principales son,
- Sintaxis y uso sencillo
- Soporte para múltiples escenarios
- Integración con herramientas de análisis
Cómo usar BenchmarkDotNet
En primer lugar, añadimos BenchmarkDotNet a nuestro proyecto mediante el paquete NuGet correspondiente
Install-Package BenchmarkDotNet
A continuación tenemos un ejemplo, extraído de la documentación de la biblioteca.
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
[SimpleJob(RuntimeMoniker.NetCoreApp30)]
[RPlotExporter]
public class Md5VsSha256
{
private SHA256 sha256 = SHA256.Create();
private MD5 md5 = MD5.Create();
private byte[] data;
[Params(1000, 10000)]
public int N;
[GlobalSetup]
public void Setup()
{
data = new byte[N];
new Random(42).NextBytes(data);
}
[Benchmark]
public byte[] Sha256() => sha256.ComputeHash(data);
[Benchmark]
public byte[] Md5() => md5.ComputeHash(data);
}
En este ejemplo, tenemos dos funciones que queremos probar, sha256.ComputeHash(data) y md5.ComputeHash(data);
En primer lugar, usamos los atributos SimpleJob para definir los benchmark para dos plataformas, Net472 y NetCore3.
Por otro lado, tenemos el parámetro N que definimos puede ser 1000 o 10000.
Finalmente, marcamos la función GlobalSetup, que se ejecutará al inicio de cada benchmark, y los dos casos de Benchmark, que son las funciones que hemos comentado anteriormente.
Si ejecutamos el código, BenchmarkDotNet nos proporciona la siguiente tabla.
Method | Runtime | N | Mean | Error | StdDev | Ratio |
---|---|---|---|---|---|---|
Sha256 | .NET 4.7.2 | 1000 | 7.735 us | 0.1913 us | 0.4034 us | 1.00 |
Sha256 | .NET Core 3.0 | 1000 | 3.989 us | 0.0796 us | 0.0745 us | 0.50 |
Md5 | .NET 4.7.2 | 1000 | 2.872 us | 0.0552 us | 0.0737 us | 1.00 |
Md5 | .NET Core 3.0 | 1000 | 1.848 us | 0.0348 us | 0.0326 us | 0.64 |
Sha256 | .NET 4.7.2 | 10000 | 74.509 us | 1.5787 us | 4.6052 us | 1.00 |
Sha256 | .NET Core 3.0 | 10000 | 36.049 us | 0.7151 us | 1.0025 us | 0.49 |
Md5 | .NET 4.7.2 | 10000 | 17.308 us | 0.3361 us | 0.4250 us | 1.00 |
Md5 | .NET Core 3.0 | 10000 | 15.726 us | 0.2064 us | 0.1930 us | 0.90 |
Como vemos es muy sencillo la definición de Benchmark y, lo que es más importante, los podemos ejecutar sabiendo que no van a ocurrir “cosas raras” que nos engañen en los resultados.
Por supuesto, la biblioteca tiene muchas más opciones, incluido generar tablas y gráficas de los resultados. Si estáis interesados, os aconsejo consultar la documentación del proyecto.
BenchmarkDotNet es Open Source, y la documentación y todo el código se encuentran disponibles en este enlace https://github.com/dotnet/BenchmarkDotNet.