Ich habe überlegt, Lazy<T>
Eigenschaften zu verwenden, um die Leistung meines eigenen Codes zu verbessern (und etwas mehr darüber zu erfahren). Ich bin hierher gekommen, um nach Antworten zu suchen, wann ich es verwenden soll, aber es scheint, dass überall, wo ich hingehe, Sätze wie:
Verwenden Sie die verzögerte Initialisierung, um die Erstellung eines großen oder ressourcenintensiven Objekts oder die Ausführung einer ressourcenintensiven Aufgabe zu verschieben, insbesondere wenn eine solche Erstellung oder Ausführung während der Lebensdauer des Programms möglicherweise nicht erfolgt.
aus der MSDN Lazy <T> -Klasse
Ich bin etwas verwirrt, weil ich nicht sicher bin, wo ich die Grenze ziehen soll. Zum Beispiel betrachte ich lineare Interpolation als eine ziemlich schnelle Berechnung, aber wenn ich es nicht tun muss, kann mir eine verzögerte Initialisierung helfen, dies zu vermeiden, und lohnt es sich?
Am Ende entschied ich mich, meinen eigenen Test zu versuchen und dachte, ich würde die Ergebnisse hier teilen. Leider bin ich kein Experte für diese Art von Tests und freue mich über Kommentare, die Verbesserungen vorschlagen.
Beschreibung
Für meinen Fall war ich besonders interessiert zu sehen, ob Lazy Properties dazu beitragen kann, einen Teil meines Codes zu verbessern, der viel interpoliert (der größte Teil davon wird nicht verwendet), und deshalb habe ich einen Test erstellt, der drei Ansätze vergleicht.
Ich habe für jeden Ansatz eine separate Testklasse mit 20 Testeigenschaften (nennen wir sie T-Eigenschaften) erstellt.
- GetInterp-Klasse: Führt jedes Mal eine lineare Interpolation aus, wenn eine t-Eigenschaft abgerufen wird.
- InitInterp-Klasse: Initialisiert die t-Eigenschaften, indem die lineare Interpolation für jede einzelne im Konstruktor ausgeführt wird. Das Get gibt nur ein Double zurück.
- InitLazy-Klasse: Richtet die t-Eigenschaften als Lazy-Eigenschaften ein, sodass die lineare Interpolation einmal ausgeführt wird, wenn die Eigenschaft zum ersten Mal abgerufen wird. Nachfolgende Gets sollten nur ein bereits berechnetes Double zurückgeben.
Die Testergebnisse werden in ms gemessen und sind der Durchschnitt von 50 Instanziierungen oder 20 Eigenschaften. Jeder Test wurde dann 5 Mal durchgeführt.
Test 1 Ergebnisse: Instanziierung (Durchschnitt von 50 Instanziierungen)
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
Test 2 Ergebnisse: First Get (durchschnittlich 20 Property Get)
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
Test 3 Ergebnisse: Zweiter Get (Durchschnitt von 20 Property Get)
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
Beobachtungen
GetInterp
ist am schnellsten wie erwartet zu instanziieren, da es nichts tut. InitLazy
ist schneller zu instanziieren als zu InitInterp
vermuten, dass der Overhead beim Einrichten von Lazy-Eigenschaften schneller ist als meine lineare Interpolationsberechnung. Ich bin hier jedoch etwas verwirrt, da InitInterp
20 lineare Interpolationen durchgeführt werden sollten (um die t-Eigenschaften festzulegen), GetInterp
die Instanziierung jedoch nur 0,09 ms dauert (Test 1), verglichen mit 0,28 ms, um nur eine lineare Interpolation durchzuführen das erste Mal (Test 2) und 0,1 ms, um es das zweite Mal zu tun (Test 3).
Es dauert InitLazy
fast zweimal länger als GetInterp
beim ersten Abrufen einer Eigenschaft, während InitInterp
es am schnellsten ist, da es seine Eigenschaften während der Instanziierung auffüllt. (Zumindest hätte es das tun sollen, aber warum war das Instanziierungsergebnis so viel schneller als eine einzelne lineare Interpolation? Wann genau werden diese Interpolationen durchgeführt?)
Leider sieht es so aus, als ob in meinen Tests eine automatische Codeoptimierung stattfindet. Es sollte GetInterp
genauso lange dauern, bis eine Eigenschaft beim ersten Mal abgerufen wird wie beim zweiten Mal, aber sie wird mehr als zweimal schneller angezeigt. Es sieht so aus, als ob diese Optimierung auch die anderen Klassen betrifft, da sie alle ungefähr die gleiche Zeit für Test 3 benötigen. Solche Optimierungen können jedoch auch in meinem eigenen Produktionscode stattfinden, was ebenfalls eine wichtige Überlegung sein kann.
Schlussfolgerungen
Während einige Ergebnisse wie erwartet sind, gibt es auch einige sehr interessante unerwartete Ergebnisse, wahrscheinlich aufgrund von Codeoptimierungen. Selbst für Klassen, die so aussehen, als würden sie viel im Konstruktor arbeiten, zeigen die Instanziierungsergebnisse, dass sie im Vergleich zum Abrufen einer doppelten Eigenschaft möglicherweise immer noch sehr schnell erstellt werden können. Während Experten auf diesem Gebiet möglicherweise in der Lage sind, Kommentare abzugeben und eingehender zu untersuchen, ist mein persönliches Gefühl, dass ich diesen Test erneut durchführen muss, jedoch anhand meines Produktionscodes, um zu untersuchen, welche Art von Optimierungen auch dort stattfinden können. Ich InitInterp
gehe jedoch davon aus, dass dies der richtige Weg sein wird.
get { if (foo == null) foo = new Foo(); return foo; }
. Und es gibt zig mögliche Orte, an denen man es benutzen kann ...