DEV Community

Cover image for Midiendo el rendimiento de nuestro código con BenchmarkDotNet
Andres Lozada Mosto
Andres Lozada Mosto

Posted on

Midiendo el rendimiento de nuestro código con BenchmarkDotNet

Un Benchmark (en Software) es un proceso que nos permite medir el rendimiento de un software, componente, algoritmo o bloque de código y verificar si cumple con los requerimientos establecidos de por ejemplo, uso de memoria o tiempos de ejecución.

Este tipo de pruebas nos permite incluso comparar 2 componentes, 2 versiones de un mismo componente o diferentes algoritmos y verificar cual tiene mejor rendimiento.

Introducción a BenchmarkDotNet

BenchmarkDotNet es la librería de .Net mas aceptada por la comunidad siendo utilizada por varios proyectos de Microsoft.

BenchmarkDotNet nos permite generar pruebas de Benchmark y ejecutarlas con la misma simplicidad de ejecución que las de testing unitario.

Entre las funcionalidades que podemos encontrar se encuentran:

  • Ejecución de multiples Benchmark
  • Setup/Cleanup entre ejecuciones
  • Parametrización (al igual que por ejemplo XUnit/NUnit)
  • Benchamark sobre diferentes plataformas/runtimes: NetFramework, Dotnet, WebAssembly, x86/x64, Mono, AOT
  • Diagnosers: de memoria, JIT, Disasembly entre otros
  • Exporters: CSV, HTML, RPlot, JSON, Markdown, etc
  • Generación de trace files para su posterior análisis en PerfView/Visual Studio Profiler/SpeedScope
  • Entre otros

Creando nuestro primer Benchmark

Nuestro Benchmark debe esta encapsulado en una clase y por cada función atómica de código a analizar le debemos agregar el atributo [Benchmark] para que la librería pueda auto-descubrir los bloques de código a ejecutar.

Usaremos Bogus para generar Fake data y el atributo [GlobalSetup]para generar una función de inicialización donde inicializamos lo que necesitamos. Ésta función se ejecutará una única vez y no afectará las métricas.

Vamos al ejemplo básico

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Bogus;

BenchmarkRunner.Run(typeof(BenchmarkFor));

public class BenchmarkFor
{
    private const int __amountOfItems = 1000;
    private string[] ListOfStrings { get; set; }

    [Benchmark(Baseline = true)]
    public int[] ForClassic()
    {
        var retValues = new int[__amountOfItems];
        for (var i = 0; i < ListOfStrings.Length; i++) 
            retValues[i] = ListOfStrings[i].Length;
        return retValues;
    }

    [Benchmark]
    public int[] ForWithLinQ() => ListOfStrings.Select(x => x.Length).ToArray();

    [GlobalSetup]
    public void Startup()
    {
        var faker = new Faker("en");
        ListOfStrings = Enumerable.Range(0, __amountOfItems)
            .Select(x => faker.Lorem.Word())
            .ToArray();
    }
}
Enter fullscreen mode Exit fullscreen mode

Hay que prestar atención que cuando se realizan estas pruebas el entorno donde se ejecutan es muy importante porque puede afectar los tiempos de gran manera. Es recomendable ejecutar el proceso desde la consola (para no attachar el debugger), compilar en modo release y no tener otros procesos corriendo en la notebook y si se puede ejecutar en un entorno aislado (ej en una instancia en cloud), mucho mejor.

Ahora sí, lo ejecutamos...

Image description

... y luego de esperar un ratito (si, tarda y genera mucho log por pantalla) obtenemos la siguiente información

Image description

Lo más importante es el último recuadro en donde vemos los tiempos promedio que tardo cada función que marcamos como [Benchmark], siendo la primera, 2x veces más rápida que la opción que utiliza Linq. En la misma, el resto de las columnas muestran la desviación estándar, la mediana y el ratio.

Algo importante a considerar en algunos casos es el Histograma que muestra para cada función. En ellos podemos ver como se distribuyeron los tiempos de las sucesivas ejecuciones y ante ciertos casos no ayudará a encontrar puntos de mejora.

Por último, se muestra la sección de Outliers que nos indica cuantos casos fueron eliminados de la muestra porque estuvieron muy por fuera del rango del resto de los casos (ya sea que estuvieron muy por debajo o muy por arriba).

Conclusión

Conocer como realizar un Benchmark de nuestro código es una habilidad muy útil para tenerla dentro de nuestro toolbox de herramientas como desarrollador y nos va a ser de mucha ayuda cuando queramos medir la performance de nuestro código.

Top comments (0)