Ich fand diesen Artikel über Lazy
: Faulheit in C # 4.0 - Faul
Was ist die beste Vorgehensweise, um mit Lazy-Objekten die beste Leistung zu erzielen? Kann mich jemand auf eine praktische Anwendung in einer realen Anwendung hinweisen? Mit anderen Worten, wann sollte ich es verwenden?
c#
.net
lazy-evaluation
Danyolgiax
quelle
quelle
get { if (foo == null) foo = new Foo(); return foo; }
. Und es gibt zig mögliche Orte, an denen man es benutzen kann ...get { if (foo == null) foo = new Foo(); return foo; }
nicht threadsicher ist, währendLazy<T>
es standardmäßig threadsicher ist.Antworten:
Sie verwenden es normalerweise, wenn Sie etwas instanziieren möchten, wenn es zum ersten Mal tatsächlich verwendet wird. Dies verzögert die Kosten für die Erstellung, bis sie benötigt werden, anstatt immer die Kosten zu verursachen.
Normalerweise ist dies vorzuziehen, wenn das Objekt verwendet werden kann oder nicht und die Kosten für dessen Konstruktion nicht trivial sind.
quelle
Lazy<T>
. Um jedoch jede Eigenschaft zu erstellen, führe ich eine lineare Interpolation (oder eine bilineare Interpolation) durch, die ziemlich trivial ist, aber einige Kosten verursacht. (Wollen Sie vorschlagen, dass ich mein eigenes Experiment mache?)Sie sollten versuchen, die Verwendung von Singletons zu vermeiden. Wenn Sie dies jedoch jemals tun müssen, können Sie
Lazy<T>
faule, threadsichere Singletons einfach implementieren:quelle
Ein großes reale Welt Beispiel, wo ein träges Laden praktisch ist , ist mit ORM (Object Relation Mapper) wie Entity Framework und NHibernate.
Angenommen, Sie haben einen Entitätskunden mit Eigenschaften für Name, Telefonnummer und Bestellungen. Name und Telefonnummer sind reguläre Zeichenfolgen, aber Bestellungen ist eine Navigationseigenschaft, die eine Liste aller Bestellungen zurückgibt, die der Kunde jemals getätigt hat.
Möglicherweise möchten Sie häufig alle Ihre Kunden durchgehen und deren Namen und Telefonnummer abrufen, um sie anzurufen. Dies ist eine sehr schnelle und einfache Aufgabe. Stellen Sie sich jedoch vor, dass jedes Mal, wenn Sie einen Kunden erstellt haben, automatisch ein komplexer Join ausgeführt wurde, um Tausende von Bestellungen zurückzugeben. Das Schlimmste ist, dass Sie die Bestellungen nicht einmal verwenden werden, was eine völlige Verschwendung von Ressourcen darstellt!
Dies ist der perfekte Ort für das verzögerte Laden, denn wenn die Order-Eigenschaft faul ist, werden nicht alle Bestellungen des Kunden abgerufen, es sei denn, Sie benötigen sie tatsächlich. Sie können die Kundenobjekte auflisten, die nur ihren Namen und ihre Telefonnummer erhalten, während die Order-Eigenschaft geduldig schläft und bereit ist, wenn Sie sie benötigen.
quelle
Db.Customers.Include("Orders")
. Dies führt dazu, dass der Order-Join zu diesem Zeitpunkt ausgeführt wird und nicht, wenn dieCustomer.Orders
Eigenschaft zum ersten Mal verwendet wird. Lazy Loading kann auch über den DbContext deaktiviert werden.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: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.
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)
Test 2 Ergebnisse: First Get (durchschnittlich 20 Property Get)
Test 3 Ergebnisse: Zweiter Get (Durchschnitt von 20 Property Get)
Beobachtungen
GetInterp
ist am schnellsten wie erwartet zu instanziieren, da es nichts tut.InitLazy
ist schneller zu instanziieren als zuInitInterp
vermuten, dass der Overhead beim Einrichten von Lazy-Eigenschaften schneller ist als meine lineare Interpolationsberechnung. Ich bin hier jedoch etwas verwirrt, daInitInterp
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 alsGetInterp
beim ersten Abrufen einer Eigenschaft, währendInitInterp
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.quelle
lazy
einige zusätzliche Buchhaltung erforderlich ist,InitLazy
würde mehr Speicher als die anderen Lösungen verbrauchen. Bei jedem Zugriff kann es auch zu geringfügigen Leistungseinbußen kommen, während geprüft wird, ob bereits ein Wert vorhanden ist oder nicht. clevere Tricks könnten diesen Aufwand beseitigen, aber es würde besondere Unterstützung in IL erfordern. (Haskell macht dies, indem er jeden faulen Wert zu einem Funktionsaufruf macht; sobald der Wert generiert ist, wird er durch eine Funktion ersetzt, die diesen Wert jedes Mal zurückgibt.)Nur um auf das Beispiel von Mathew zu verweisen
Bevor der Faule geboren wurde, hätten wir es so gemacht:
quelle
Von MSDN:
Zusätzlich zur Antwort von James Michael Hare bietet Lazy eine thread-sichere Initialisierung Ihres Werts. Werfen Sie einen Blick auf LazyThreadSafetyMode Aufzählung MSDN Eintrag beschreibt verschiedene Arten von Thread - Sicherheit Modi für diese Klasse.
quelle
Sie sollten sich dieses Beispiel ansehen, um die Lazy Loading-Architektur zu verstehen
-> Ausgabe -> 0 1 2
aber wenn dieser Code nicht schreibt "list.Value.Add (0);"
Ausgabe -> Wert nicht erstellt
quelle