J'ai trouvé cet article sur Lazy
: La paresse en C # 4.0 - Lazy
Quelle est la meilleure pratique pour obtenir les meilleures performances en utilisant des objets Lazy? Quelqu'un peut-il m'indiquer une utilisation pratique dans une application réelle? En d'autres termes, quand dois-je l'utiliser?
c#
.net
lazy-evaluation
danyolgiax
la source
la source
get { if (foo == null) foo = new Foo(); return foo; }
. Et il y a des millions d'endroits possibles pour l'utiliser ...get { if (foo == null) foo = new Foo(); return foo; }
n'est pas thread-safe, tandis queLazy<T>
thread-safe par défaut.Réponses:
Vous l'utilisez généralement lorsque vous souhaitez instancier quelque chose la première fois qu'il est réellement utilisé. Cela retarde le coût de sa création jusqu'à si / quand il est nécessaire au lieu de toujours encourir le coût.
Habituellement, cela est préférable lorsque l'objet peut être utilisé ou non et que le coût de sa construction n'est pas négligeable.
la source
Lazy<T>
. Cependant, pour créer chaque propriété, je fais une interpolation linéaire (ou une interpolation bilinéaire) qui est assez triviale mais a un certain coût. (Allez-vous suggérer que j'aille faire ma propre expérience?)Vous devriez essayer d'éviter d'utiliser des singletons, mais si jamais vous en avez besoin, cela
Lazy<T>
facilite l'implémentation de singletons paresseux et thread-safe:la source
Un excellent exemple du monde réel où le chargement paresseux est utile est avec les ORM (Object Relation Mappers) tels que Entity Framework et NHibernate.
Supposons que vous ayez une entité Client qui possède des propriétés pour le nom, le numéro de téléphone et les commandes. Name et PhoneNumber sont des chaînes régulières, mais Orders est une propriété de navigation qui renvoie une liste de toutes les commandes que le client a passées.
Vous voudrez souvent passer par tous vos clients et obtenir leur nom et numéro de téléphone pour les appeler. Il s'agit d'une tâche très rapide et simple, mais imaginez que chaque fois que vous créez un client, il passe automatiquement et effectue une jointure complexe pour renvoyer des milliers de commandes. Le pire, c'est que vous n'allez même pas utiliser les commandes, c'est donc un gaspillage complet de ressources!
C'est l'endroit parfait pour le chargement paresseux, car si la propriété Order est paresseuse, elle n'ira pas chercher toute la commande du client, sauf si vous en avez réellement besoin. Vous pouvez énumérer les objets Client en obtenant uniquement leur nom et leur numéro de téléphone pendant que la propriété Order dort patiemment, prête lorsque vous en avez besoin.
la source
Db.Customers.Include("Orders")
. Cela entraînera l'exécution de la jointure d'ordre à ce moment plutôt qu'à laCustomer.Orders
première utilisation de la propriété. Le chargement différé peut également être désactivé via DbContext.J'ai envisagé d'utiliser des
Lazy<T>
propriétés pour améliorer les performances de mon propre code (et pour en savoir un peu plus). Je suis venu ici pour chercher des réponses sur le moment de l'utiliser, mais il semble que partout où je vais, il y a des phrases comme:de MSDN Lazy <T> Class
Je suis un peu confus car je ne sais pas où tracer la ligne. Par exemple, je considère l'interpolation linéaire comme un calcul assez rapide, mais si je n'ai pas besoin de le faire, l'initialisation paresseuse peut-elle m'aider à éviter de le faire et cela en vaut-il la peine?
Finalement, j'ai décidé d'essayer mon propre test et j'ai pensé partager les résultats ici. Malheureusement, je ne suis pas vraiment un expert dans ce genre de tests et je suis donc heureux d'obtenir des commentaires suggérant des améliorations.
La description
Pour mon cas, j'étais particulièrement intéressé de voir si Lazy Properties pouvait aider à améliorer une partie de mon code qui fait beaucoup d'interpolation (la plupart étant inutilisée) et j'ai donc créé un test qui comparait 3 approches.
J'ai créé une classe de test distincte avec 20 propriétés de test (appelons-les propriétés t) pour chaque approche.
Les résultats des tests sont mesurés en ms et correspondent à la moyenne de 50 instanciations ou 20 propriétés obtenues. Chaque test a ensuite été exécuté 5 fois.
Résultats du test 1: Instanciation (moyenne de 50 instanciations)
Résultats du test 2: première acquisition (moyenne de 20 propriétés)
Résultats du test 3: Second Get (moyenne de 20 propriétés)
Observations
GetInterp
est plus rapide à instancier comme prévu car il ne fait rien.InitLazy
est plus rapide à instancier que deInitInterp
suggérer que la surcharge dans la configuration des propriétés paresseuses est plus rapide que mon calcul d'interpolation linéaire. Cependant, je suis un peu confus ici carInitInterp
devrait faire 20 interpolations linéaires (pour configurer ses propriétés t) mais il ne faut que 0,09 ms pour instancier (test 1), par rapport àGetInterp
ce qui prend 0,28 ms pour effectuer une seule interpolation linéaire la première fois (test 2), et 0,1 ms pour le faire la deuxième fois (test 3).Cela prend
InitLazy
presque 2 fois plus de temps queGetInterp
d'obtenir une propriété la première fois, tandis queInitInterp
c'est le plus rapide, car il a rempli ses propriétés lors de l'instanciation. (Au moins, c'est ce qu'il aurait dû faire, mais pourquoi son résultat d'instanciation était-il tellement plus rapide qu'une simple interpolation linéaire? Quand fait-il exactement ces interpolations?)Malheureusement, il semble qu'il y ait une optimisation automatique du code en cours dans mes tests. Cela devrait prendre
GetInterp
le même temps pour obtenir une propriété la première fois que pour la deuxième fois, mais il apparaît plus de 2 fois plus rapidement. Il semble que cette optimisation affecte également les autres classes car elles prennent toutes à peu près le même temps pour le test 3. Cependant, de telles optimisations peuvent également avoir lieu dans mon propre code de production, ce qui peut également être une considération importante.Conclusions
Bien que certains résultats soient conformes aux attentes, il existe également des résultats inattendus très intéressants probablement dus à des optimisations de code. Même pour les classes qui semblent faire beaucoup de travail dans le constructeur, les résultats de l'instanciation montrent qu'ils peuvent toujours être très rapides à créer, par rapport à l'obtention d'une double propriété. Bien que les experts dans ce domaine puissent commenter et enquêter de manière plus approfondie, mon sentiment personnel est que je dois refaire ce test mais sur mon code de production afin d'examiner quel type d'optimisations peuvent également y avoir lieu. Cependant, je m'attends à ce que ce
InitInterp
soit la voie à suivre.la source
lazy
doit faire une comptabilité supplémentaire,InitLazy
utiliserait plus de mémoire que les autres solutions. Il peut également avoir un impact mineur sur les performances de chaque accès, tout en vérifiant s'il a déjà une valeur ou non; des astuces intelligentes pourraient supprimer cette surcharge, mais cela nécessiterait un support spécial en IL. (Haskell le fait en faisant de chaque valeur paresseuse un appel de fonction; une fois la valeur générée, elle est remplacée par une fonction qui renvoie cette valeur à chaque fois.)Juste pour montrer l'exemple affiché par Mathew
avant la naissance du Lazy, nous l'aurions fait de cette façon:
la source
Depuis MSDN:
En plus de la réponse de James Michael Hare, Lazy fournit une initialisation thread-safe de votre valeur. Jetez un œil à l' entrée MSDN de l'énumération LazyThreadSafetyMode décrivant divers types de modes de sécurité des threads pour cette classe.
la source
Vous devriez regarder cet exemple pour comprendre l'architecture de chargement paresseux
-> sortie -> 0 1 2
mais si ce code n'écrit pas "list.Value.Add (0);"
sortie -> Valeur non créée
la source