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:
Utilisez l'initialisation paresseuse pour différer la création d'un objet volumineux ou gourmand en ressources, ou l'exécution d'une tâche gourmande en ressources, en particulier lorsqu'une telle création ou exécution risque de ne pas se produire pendant la durée de vie du programme.
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.
- Classe GetInterp: exécute une interpolation linéaire chaque fois qu'une propriété t est obtenue.
- Classe InitInterp: Initialise les propriétés t en exécutant l'interpolation linéaire pour chacune dans le constructeur. Le get retourne juste un double.
- Classe InitLazy: définit les propriétés t en tant que propriétés paresseuses afin que l'interpolation linéaire soit exécutée une fois lors de l' obtention de la propriété. Les get suivants devraient simplement renvoyer un double déjà calculé.
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)
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
Résultats du test 2: première acquisition (moyenne de 20 propriétés)
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
Résultats du test 3: Second Get (moyenne de 20 propriétés)
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
Observations
GetInterp
est plus rapide à instancier comme prévu car il ne fait rien. InitLazy
est plus rapide à instancier que de InitInterp
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 car InitInterp
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 que GetInterp
d'obtenir une propriété la première fois, tandis que InitInterp
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.
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.