csharp-benchmark-net

Cómo hacer pruebas de rendimiento en C# con BenchmarkDotNet

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.

Además, su uso es muy sencillo, y no demasiado diferente a realizar un Testing. 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.

MethodRuntimeNMeanErrorStdDevRatio
Sha256.NET 4.7.210007.735 us0.1913 us0.4034 us1.00
Sha256.NET Core 3.010003.989 us0.0796 us0.0745 us0.50
Md5.NET 4.7.210002.872 us0.0552 us0.0737 us1.00
Md5.NET Core 3.010001.848 us0.0348 us0.0326 us0.64
Sha256.NET 4.7.21000074.509 us1.5787 us4.6052 us1.00
Sha256.NET Core 3.01000036.049 us0.7151 us1.0025 us0.49
Md5.NET 4.7.21000017.308 us0.3361 us0.4250 us1.00
Md5.NET Core 3.01000015.726 us0.2064 us0.1930 us0.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.