Encontré este artículo sobre Lazy
: Pereza en C # 4.0 - Perezoso
¿Cuál es la mejor práctica para tener el mejor rendimiento con objetos perezosos? ¿Alguien puede señalarme un uso práctico en una aplicación real? En otras palabras, ¿cuándo debería usarlo?
c#
.net
lazy-evaluation
danyolgiax
fuente
fuente
get { if (foo == null) foo = new Foo(); return foo; }
. Y hay millones de lugares posibles para usarlo ...get { if (foo == null) foo = new Foo(); return foo; }
no es seguro para subprocesos, mientras queLazy<T>
es seguro para subprocesos de forma predeterminada.Respuestas:
Por lo general, lo usa cuando desea crear una instancia de algo la primera vez que se usa realmente. Esto retrasa el costo de crearlo hasta que sea necesario en lugar de incurrir siempre en el costo.
Por lo general, esto es preferible cuando el objeto puede o no usarse y el costo de construirlo no es trivial.
fuente
Lazy<T>
. Sin embargo, para crear cada propiedad, estoy haciendo una interpolación lineal (o una interpolación bilineal) que es bastante trivial pero tiene algún costo. (¿Vas a sugerirme que vaya y haga mi propio experimento?)Debe intentar evitar el uso de Singletons, pero si alguna vez lo necesita,
Lazy<T>
facilita la implementación de Singletons perezosos y seguros para subprocesos:fuente
Un gran ejemplo del mundo real de donde la carga diferida es útil es con ORM (Object Relation Mappers) como Entity Framework y NHibernate.
Supongamos que tiene una entidad Cliente que tiene propiedades para Nombre, Número de teléfono y Pedidos. Nombre y número de teléfono son cadenas regulares, pero Pedidos es una propiedad de navegación que devuelve una lista de todos los pedidos que el cliente haya realizado.
A menudo, es posible que desee consultar a todos sus clientes y obtener su nombre y número de teléfono para llamarlos. Esta es una tarea muy rápida y simple, pero imagínese si cada vez que crea un cliente se realiza automáticamente y se une de manera compleja para devolver miles de pedidos. ¡La peor parte es que ni siquiera va a usar las órdenes, por lo que es un desperdicio completo de recursos!
Este es el lugar perfecto para la carga diferida porque si la propiedad Order es diferida, no obtendrá todos los pedidos del cliente a menos que realmente los necesite. Puede enumerar los objetos del Cliente obteniendo solo su Nombre y Número de teléfono mientras la propiedad Pedido está durmiendo pacientemente, lista para cuando la necesite.
fuente
Db.Customers.Include("Orders")
. Esto hará que la unión de órdenes se ejecute en ese momento en lugar de cuando laCustomer.Orders
propiedad se usa por primera vez. La carga diferida también se puede deshabilitar a través de DbContext.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: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.
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)
Resultados de la Prueba 2: Primera obtención (promedio de 20 propiedades obtenidas)
Resultados de la Prueba 3: Segunda obtención (promedio de 20 propiedades obtenidas)
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 queInitInterp
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í porqueInitInterp
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 loGetInterp
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 queGetInterp
obtener una propiedad la primera vez, mientras queInitInterp
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.fuente
lazy
tiene que hacer un poco de contabilidad adicional,InitLazy
usaría más memoria que las otras soluciones. También puede tener un impacto de rendimiento menor en cada acceso, mientras verifica si ya tiene un valor o no; Los trucos inteligentes podrían eliminar esa sobrecarga, pero requeriría un soporte especial en IL. (Haskell hace esto haciendo cada valor perezoso una llamada de función, una vez que se genera el valor, y se sustituye por una función que devuelve ese valor cada vez.)Solo para señalar el ejemplo publicado por Mathew
antes de que naciera el perezoso lo hubiéramos hecho de esta manera:
fuente
De MSDN:
Además de la respuesta de James Michael Hare, Lazy proporciona una inicialización segura de su valor. Eche un vistazo a la entrada MSDN de enumeración LazyThreadSafetyMode que describe varios tipos de modos de seguridad de subprocesos para esta clase.
fuente
Debería mirar este ejemplo para comprender la arquitectura de carga diferida
-> salida -> 0 1 2
pero si este código no escribe "list.Value.Add (0);"
salida -> Valor no creado
fuente