He estado considerando usar Lazy<T>
propiedades para ayudar a mejorar el rendimiento de mi propio código (y para aprender un poco más al respecto). Vine aquí buscando respuestas sobre cuándo usarlo, pero parece que donde quiera que vaya hay frases como:
Utilice la inicialización diferida para diferir la creación de un objeto grande o que requiera muchos recursos, o la ejecución de una tarea que requiera muchos recursos, particularmente cuando tal creación o ejecución podría no ocurrir durante la vida útil del programa.
de la clase MSDN Lazy <T>
Me siento un poco confundido porque no estoy seguro de dónde dibujar la línea. Por ejemplo, considero la interpolación lineal como un cálculo bastante rápido, pero si no necesito hacerlo, ¿puede la inicialización perezosa ayudarme a evitar hacerlo? ¿Vale la pena?
Al final decidí probar mi propia prueba y pensé en compartir los resultados aquí. Desafortunadamente, no soy realmente un experto en hacer este tipo de pruebas y estoy feliz de recibir comentarios que sugieran mejoras.
Descripción
En mi caso, estaba particularmente interesado en ver si Lazy Properties podría ayudar a mejorar una parte de mi código que hace mucha interpolación (la mayor parte no se usa) y, por lo tanto, he creado una prueba que comparó 3 enfoques.
Creé una clase de prueba separada con 20 propiedades de prueba (llamémoslas propiedades t) para cada enfoque.
- Clase GetInterp: ejecuta interpolación lineal cada vez que se obtiene una propiedad t.
- Clase InitInterp: Inicializa las propiedades t ejecutando la interpolación lineal para cada una en el constructor. El get solo devuelve un doble.
- Clase InitLazy: configura las propiedades t como propiedades Lazy para que la interpolación lineal se ejecute una vez cuando se obtiene la propiedad por primera vez. Los resultados posteriores deberían devolver un doble ya calculado.
Los resultados de la prueba se miden en ms y son el promedio de 50 instancias o 20 propiedades obtenidas. Cada prueba se ejecutó 5 veces.
Resultados de la prueba 1: creación de instancias (promedio de 50 instancias)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72
InitInterp 0.08481 0.084908 0.099328 0.098626 0.083774 0.0902892 100.00
InitLazy 0.058436 0.05891 0.068046 0.068108 0.060648 0.0628296 69.59
Resultados de la Prueba 2: Primera obtención (promedio de 20 propiedades obtenidas)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.263 0.268725 0.31373 0.263745 0.279675 0.277775 54.38
InitInterp 0.16316 0.161845 0.18675 0.163535 0.173625 0.169783 33.24
InitLazy 0.46932 0.55299 0.54726 0.47878 0.505635 0.510797 100.00
Resultados de la Prueba 3: Segunda obtención (promedio de 20 propiedades obtenidas)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.08184 0.129325 0.112035 0.097575 0.098695 0.103894 85.30
InitInterp 0.102755 0.128865 0.111335 0.10137 0.106045 0.110074 90.37
InitLazy 0.19603 0.105715 0.107975 0.10034 0.098935 0.121799 100.00
Observaciones
GetInterp
es más rápido crear instancias como se esperaba porque no está haciendo nada. InitLazy
es más rápido crear instancias que InitInterp
sugerir que la sobrecarga en la configuración de propiedades diferidas es más rápida que mi cálculo de interpolación lineal. Sin embargo, estoy un poco confundido aquí porque InitInterp
debería estar haciendo 20 interpolaciones lineales (para configurar sus propiedades t), pero solo toma 0.09 ms para crear una instancia (prueba 1), en comparación con lo GetInterp
que toma 0.28 ms para hacer una sola interpolación lineal la primera vez (prueba 2), y 0.1 ms para hacerlo la segunda vez (prueba 3).
Se tarda InitLazy
casi 2 veces más que GetInterp
obtener una propiedad la primera vez, mientras que InitInterp
es la más rápida, ya que poblaba sus propiedades durante la creación de instancias. (Al menos eso es lo que debería haber hecho, pero ¿por qué el resultado de su instanciación fue mucho más rápido que una sola interpolación lineal? ¿Cuándo exactamente está haciendo estas interpolaciones?)
Desafortunadamente, parece que hay una optimización automática de código en mis pruebas. Debería tomar GetInterp
el mismo tiempo obtener una propiedad la primera vez que la segunda, pero se muestra más de 2 veces más rápido. Parece que esta optimización también está afectando a las otras clases, ya que todas toman aproximadamente la misma cantidad de tiempo para la prueba 3. Sin embargo, tales optimizaciones también pueden tener lugar en mi propio código de producción, que también puede ser una consideración importante.
Conclusiones
Si bien algunos resultados son los esperados, también hay algunos resultados inesperados muy interesantes, probablemente debido a las optimizaciones de código. Incluso para las clases que parecen estar haciendo mucho trabajo en el constructor, los resultados de creación de instancias muestran que aún pueden ser muy rápidos de crear, en comparación con obtener una propiedad doble. Si bien los expertos en este campo pueden comentar e investigar más a fondo, mi sensación personal es que necesito hacer esta prueba nuevamente, pero en mi código de producción para examinar qué tipo de optimizaciones pueden estar teniendo lugar allí también. Sin embargo, espero que ese InitInterp
sea el camino a seguir.
get { if (foo == null) foo = new Foo(); return foo; }
. Y hay millones de lugares posibles para usarlo ...