martes, 14 de junio de 2016

Mis performance tips… (son realmente míos?) Parte 1. Mide

 

Hola a todos, el miércoles 25 de Mayo estuve en una sesión de @mscoders madrid, donde Luis Ruiz nos dio algunos consejos útiles relacionados con el rendimiento de nuestros desarrollos. Vamos a ver alguno de ellos… y algunos mas de mi cosecha.

Midiendo que es gerundio:

Lo primero cuando hablamos de performance es medir, o mas bien ser capaz de medir las mejoras que vamos implementando. Tenemos para ello una herramienta sencilla y muy poderosa: BenchMarkDotNet, que se va a encargar de ejecutar nuestro código varias veces, analizar su rendimiento y otros factores, y presentarnos los  resultados en diversos formatos. Vamos a dedicar este post a usarlo, utilizando un ejemplo código que enlaza con uno de los primeros post de este blog, que comparaba distintos métodos para clonar objetos: Clone Wars (o el arte de clonar objetos). Parte 1

Compararemos el rendimiento al clonar un objetos pesado, utilizando dos metodos diferentes que explicaba en mi post: Mediante serialización y mediante reflexión.

BecnMarkDotNet, empezando

El readme que vais a encontrar en GitHub explica todo bastante bien, por lo que ahora me voy a centrar en lo mínimo que necesito para echar a andar el ejemplo. Más delante en esta serie veremos aspectos mas avanzados y como resolver algún problemilla que he encontrado por el camino

Una vez que dispones del código que quieres testear los pasos son los siguientes:

  • Añade el paquete via Nuget

image

  • Crea una clase “tester”

En esta clase debemos decorar los métodos que se van a medir con el atributo Benchmark.  En mi caso, si quiero comparar dos modos de clonar un objeto, voy a crear dos métodos, cada uno se encargará de realizar el clonado de una de las maneras:

[Benchmark]
public void CloneByReflection()
{
    var cloner = new ReflectionCloner();
    var garage = CreateGarage(numberOfVehicles: 10);
    var res = cloner.Clone<Garage>(garage);
}

[Benchmark]
public void CloneBySerialization()
{
    var cloner = new SerializationCloner();
    var garage = CreateGarage(numberOfVehicles: 10);
    var res = cloner.Clone<Garage>(garage);
}

 

  • Invoca tu test

Es sencillo, solo hay que utilizar el BencharkRunner que nos proporciona el framework

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<CloneTester>();
    }      
      
}
  • Analiza los resultados

image

En este caso vemos lo que esperábamosy ya vimos allá por finales de 2012: la clonación por serialización es más rápida, no mucho pero más rápida que el método basado en reflection.

¿Como lo hace BenchMarkDotNet? Realiza varias ejecuciones de los métodos marcados, para finalmente calcular medias y desviaciones, y presentarnos los resultados. Todo esto se puedo controlar y configurar, vamos a empezar con ello.

Parámetros

Me pregunto, ¿obtendré el mismo resultado en base al objeto clonado (que sea más grande o más pequeño)?

Que pasará si realizo la misma operación de clonado varias veces seguidas, ¿seguirá siendo el clonado por serialización más rápido?

Esto lo podemos manejar con los parámetros que BenchMarkDotNet nos ofrece. Voy a añadir 2 propiedades a mi código, una para indicar el tamaño del objeto a clonar (número de vehículos del garaje) y otro para controlar en numero de clonaciones que voy a ejecutar.

Decorándolas con el atributo Params puedo especificarle distintos valores que BenchMarkDotNet usará como combinaciones para los tests… ¿fácil verdad?

[Params(100, 200, 2000)]
public int NumTimes { get; set; }

[Params(2, 5, 15)]
public int NumVehicles { get; set; }


[Benchmark]
public void CloneByReflection()
{
    var cloner = new ReflectionCloner();
    var garage = CreateGarage(numberOfVehicles: NumVehicles);
    for (var i = 0; i < NumTimes; i++)
    {
        var res = cloner.Clone<Garage>(garage);
    }
}

[Benchmark]
public void CloneBySerialization()
{
    var cloner = new SerializationCloner();
    var garage = CreateGarage(numberOfVehicles: NumVehicles);
    for (var i = 0; i < NumTimes; i++)
    {
        var res = cloner.Clone<Garage>(garage);
    }
}

¿Que hemos conseguido?  Que BenchMarkDotNet se encarga de ejecutar todas las combinaciones posibles, es decir:

  • 100 clonados de un garaje con 2 vehículos
  • 100 clonados de un garaje con 5 vehículos
  • 100 clonados de un garaje con 15 vehículos
  • 200 clonados de un garaje con 2 vehículos
  • 200 clonados de un garaje con 5 vehículos
  • 200 clonados de un garaje con 15 vehículos
  • 2000 clonados de un garaje con 2 vehículos
  • 2000 clonados de un garaje con 5 vehículos
  • 2000 clonados de un garaje con 15 vehículos

Aquí tenéis el resultado, como veis las cosas han cambiado. El método de clonado por serialización no siempre gana…  si el objeto a clonar es pequeño la reflexión puede ser una opción (ojo, si solo tenemos en cuenta una variable: el tiempo)

image

 

Podéis encontrar en código en github: https://github.com/snavarropino/PerformanceTips

 

Continuará …

No hay comentarios:

Publicar un comentario