DEV Community

Andres Lozada Mosto
Andres Lozada Mosto

Posted on

Midiendo el rendimiento de nuestro código con BenchmarkDotNet - MemoryDiagnoser

En el primer artículo de esta serie introducimos el concepto de Benchmark y como crear un test de rendimiento en C#. Es el momento de entender como analizar la utilización de la memoria y del GC de .Net.

Utilización de MemoryDiagnoser

En este caso vamos a utilizar un Benchmark diferente al que utilizamos anteriormente para que nos permita ver cuanta memoria es utilizada y cuantas veces se ejecuta el GC.

Para ver éstas métricas es necesario agregar el atributo [MemoryAnaliser] a nuestra clase.

BenchmarkRunner.Run(typeof(StringVsSpan));

[MemoryDiagnoser]
public class StringVsSpan
{
    private string[] ListOfStrings { get; set; }

    [Benchmark]
    public string concatStrings_concat()
    {
        var retValue = string.Empty;
        for (int i = 0; i < ListOfStrings.Length; i++)
            retValue += ListOfStrings[i];

        return retValue;
    }

    [Benchmark]
    public string concatStrings_StringBuilder()
    {
        var sb = new StringBuilder();
        for(int i = 0; i < ListOfStrings.Length; i++)
            sb.Append(ListOfStrings[i]);

        return sb.ToString();
    }

    [Benchmark]
    public string concatStrings_Join()
    {
        return String.Join("", ListOfStrings);
    }

    [GlobalSetup]
    public void Startup()
    {
        var faker = new Faker("en");
        ListOfStrings = faker.Lorem.Paragraphs(50, "\n\n").Split("\n\n");
    }
}
Enter fullscreen mode Exit fullscreen mode

Lo ejecutamos ..

Image description

... y obtenemos el siguiente resultado

Image description

Se ven 2 nuevas columnas, Gen0 y Allocation

Allocation
Es la cantidad de memoria en el Heap utilizada para correr la función que estamos probando por cada iteración

Gen0/Gen1/Gen2
En donde Gen0 es la ejecución del GC donde recolecta objetos de corta duración y se ejecuta de forma mas periódica. En este caso no tenemos Gen1 ni Gen2, pero estas ejecuciones del GC recolectan objetos mas "pesados" y que tienen un ciclo de vida más largos.

En nuestro resultado podemos concluir que la manera más eficiente de concatenar strings (dentro de las alternativas que se evaluaron) es utilizando String.Join porque genera menos ejecuciones del GC de primer nivel y utiliza menos alocación de memoria en el Heap.

Conclusión

Agregando el análisis de memoria a nuestros tests de benchmark podemos identificar como se comporta, en manera general, diferentes estrategias para obtener un mismo resultado y darnos major información para elegir una y otra.

Top comments (0)