Mëssgléckt Artikel iwwer d'Beschleunegung vun der Reflexioun

Ech erklären direkt den Titel vum Artikel. Den urspréngleche Plang war gutt, zouverlässeg Berodung ze ginn, wéi een d'Notzung vun der Reflexioun mat engem einfachen awer realistesche Beispill beschleunegt, awer während dem Benchmarking huet sech erausgestallt datt d'Reflexioun net sou lues ass wéi ech geduecht hunn, LINQ ass méi lues wéi a mengen Albtraum. Mä um Enn huet et sech erausgestallt datt ech och e Feeler an de Miessunge gemaach hunn ... Detailer vun dëser Liewensgeschicht sinn ënner dem Schnëtt an an de Kommentarer. Well d'Beispill zimmlech allgemeng ass an am Prinzip ëmgesat gëtt wéi et normalerweis an enger Entreprise gemaach gëtt, ass et eng ganz interessant, wéi et mir schéngt, Liewensdemonstratioun erausgestallt: den Impakt op d'Geschwindegkeet vum Haaptthema vum Artikel war net bemierkbar wéinst externer Logik: Moq, Autofac, EF Core an aner "Bänner".

Ech hunn ugefaang ënner dem Androck vun dësem Artikel ze schaffen: Firwat ass Reflexioun lues

Wéi Dir gesitt, proposéiert den Auteur kompiléiert Delegéierten ze benotzen anstatt direkt Reflexiounsartmethoden ze ruffen als e super Wee fir d'Applikatioun staark ze beschleunegen. Et gëtt, natierlech, IL Emissioun, mee ech géif gären ze vermeiden, well dat ass déi Aarbechtsintensiv Manéier d'Aufgab ze Leeschtunge, déi mat Feeler voll ass.

Bedenkt datt ech ëmmer eng ähnlech Meenung iwwer d'Geschwindegkeet vun der Reflexioun gehal hunn, hunn ech net besonnesch geduecht d'Conclusiounen vum Auteur ze froen.

Ech begéinen dacks naiv Notzung vu Reflexioun an der Entreprise. Den Typ gëtt geholl. Informatioun iwwer d'Propriétéit gëtt geholl. D'SetValue Method gëtt genannt a jidderee freet sech. De Wäert ass am Zilfeld ukomm, jiddereen ass frou. Ganz schlau Leit - Senioren an Teamleit - schreiwen hir Extensiounen op Objet, baséiert op sou enger naiver Ëmsetzung "Universal" Mappers vun enger Aart op déi aner. D'Essenz ass normalerweis dëst: mir huelen all d'Felder, huelen all d'Eegeschafte, iteréieren iwwer si: wann d'Nimm vun den Typmemberen passen, maache mir SetValue aus. Vun Zäit zu Zäit fanne mir Ausnahmen wéinst Feeler, wou mir net e puer Eegentum an enger vun den Typen fonnt hunn, awer och hei gëtt et e Wee eraus, deen d'Leeschtung verbessert. Probéieren / fänken.

Ech hunn d'Leit gesinn d'Parser an d'Mapper nei erfannen ouni voll bewaffnet mat Informatioun iwwer wéi d'Maschinnen, déi virun hinnen komm sinn, funktionnéieren. Ech hunn d'Leit gesinn hir naiv Implementatioune hannert Strategien, hannert Schnëttplazen, hannert Injektiounen verstoppen, wéi wann dëst déi spéider Bacchanalia géif entschëllegen. Ech hunn d'Nues op esou Erkenntnisser gedréint. Tatsächlech hunn ech de richtege Leeschtungsleck net gemooss, a wa méiglech, hunn ech d'Ëmsetzung einfach op eng méi "optimal" geännert, wann ech meng Hänn drop kréien. Dofir hunn déi éischt Miessunge, déi hei ënnen diskutéiert ginn, mech eescht duerchernee gemaach.

Ech denken, datt vill vun iech, Richter oder aner Ideologen liesen, op eng komplett fair Ausso begéint sinn datt Reflexioun am Code e Phänomen ass, deen en extrem negativen Impakt op d'Leeschtung vun der Applikatioun huet.

Reflexioun nennen zwéngt den CLR duerch Versammlungen ze goen fir deen ze fannen deen se brauchen, hir Metadaten opzéien, se parséieren, asw. Zousätzlech féiert d'Reflexioun beim Traverséiere vun Sequenzen zu der Allokatioun vun enger grousser Quantitéit un Erënnerung. Mir benotzen d'Erënnerung, CLR entdeckt de GC a Frise fänken un. Et sollt merkbar lues sinn, gleeft mir. Déi enorm Quantitéiten un Erënnerung op modernen Produktiounsserveren oder Cloud Maschinnen verhënneren net héich Veraarbechtungsverzögerungen. Tatsächlech, wat méi Erënnerung, wat méi wahrscheinlech Dir sidd NOTICE wéi de GC funktionnéiert. Reflexioun ass, an der Theorie, eng extra rout Lapp fir hien.

Wéi och ëmmer, mir benotzen all IoC Behälter an Datummapper, de Betribsprinzip vun deem ass och op Reflexioun baséiert, awer et gi meeschtens keng Froen iwwer hir Leeschtung. Nee, net well d'Aféierung vun Ofhängegkeeten an Abstraktioun vun externen begrenzten Kontextmodeller sou néideg sinn, datt mir op alle Fall d'Leeschtung mussen opferen. Alles ass méi einfach - et beaflosst wierklech d'Performance net vill.

D'Tatsaach ass datt déi meescht üblech Kaderen, déi op Reflexiounstechnologie baséieren, all Zorte vun Tricken benotzen fir méi optimal domat ze schaffen. Normalerweis ass dëst e Cache. Typesch sinn dës Ausdréck an Delegéiert aus dem Ausdrockbaum kompiléiert. Deeselwechten Automapper hält e kompetitivt Wierderbuch dat Typen mat Funktiounen entsprécht déi een an en anert konvertéieren ouni Reflexioun ze ruffen.

Wéi gëtt dat erreecht? Wesentlech ass dëst net anescht wéi d'Logik déi d'Plattform selwer benotzt fir JIT Code ze generéieren. Wann eng Method fir d'éischte Kéier genannt gëtt, gëtt se kompiléiert (an, jo, dëse Prozess ass net séier); op spéider Uruff gëtt d'Kontroll op déi scho kompiléiert Method transferéiert, an et gëtt keng bedeitend Leeschtungsreduktiounen.

An eisem Fall kënnt Dir och JIT Kompilatioun benotzen an dann dat kompiléiert Verhalen mat der selwechter Leeschtung wéi seng AOT Géigeparteien benotzen. Ausdréck wäert eis an dësem Fall hëllefen.

De Prinzip an der Fro kann kuerz wéi follegt formuléiert ginn:
Dir sollt d'Finale Resultat vun der Reflexioun als Delegéierte mat der kompiléierter Funktioun cache. Et mécht och Sënn fir all néideg Objete mat Informatioun iwwer Typen an de Felder vun Ärem Typ, dem Aarbechter, ze cache, déi ausserhalb vun den Objete gespäichert sinn.

Et gëtt Logik an dësem. De gesonde Mënscheverstand seet eis datt wann eppes ka kompiléiert a cache ka ginn, da sollt et gemaach ginn.

Wann Dir no vir kuckt, sollt et gesot ginn datt de Cache bei der Aarbecht mat der Reflexioun seng Virdeeler huet, och wann Dir déi proposéiert Method fir Ausdréck net ze kompiléieren benotzt. Eigentlech widderhuelen ech hei einfach d'Thes vum Auteur vum Artikel, op deen ech uewen bezéien.

Elo iwwer de Code. Loosst eis e Beispill kucken, dat baséiert op menger rezenter Péng, déi ech an enger seriöer Produktioun vun enger seriöer Kredittinstitut konfrontéiert hunn. All Entitéite si fiktiv sou datt kee géif roden.

Et gëtt e puer Essenz. Loosst et Kontakt ginn. Et gi Bréiwer mat engem standardiséierte Kierper, aus deem de Parser an den Hydrator déiselwecht Kontakter erstellen. E Bréif ass ukomm, mir hunn et gelies, et a Schlësselwäertpaaren parséiert, e Kontakt erstallt an an der Datebank gespäichert.

Et ass elementar. Loosst eis soen datt e Kontakt d'Eegeschafte Voll Numm, Alter a Kontakt Telefon huet. Dës Donnéeë ginn am Bréif iwwerdroen. D'Geschäft wëll och Ënnerstëtzung fir séier nei Schlësselen ze addéieren fir Entitéitseigenschaften a Pairen am Kierper vum Bréif ze kartéieren. Am Fall wou een en Tippfeeler an der Schabloun gemaach huet oder wann et virun der Verëffentlechung néideg ass dréngend d'Mapping vun engem neie Partner unzepassen, un dat neit Format unzepassen. Da kënne mir eng nei Kartéierungskorrelatioun als bëlleg Datefix addéieren. Dat ass, e Liewen Beispill.

Mir implementéieren, erstellen Tester. Wierker.

Ech wäert de Code net ubidden: et gi vill Quellen, a si sinn op GitHub iwwer de Link um Enn vum Artikel verfügbar. Dir kënnt se lueden, se iwwer Unerkennung torturéieren a moossen, wéi et an Ärem Fall beaflosst. Ech ginn nëmmen de Code vun zwee Schablounmethoden, déi den Hydrator ënnerscheeden, dee séier sollt sinn, vum Hydrator, dee lues sollt sinn.

D'Logik ass wéi follegt: d'Schablounmethod kritt Pairen generéiert vun der Basis Parser Logik. D'LINQ Schicht ass de Parser an d'BasisLogik vum Hydrator, deen eng Ufro un den Datebankkontext mécht a Schlëssele mat Pairen vum Parser vergläicht (fir dës Funktiounen gëtt et Code ouni LINQ zum Verglach). Als nächst ginn d'Paaren op d'Haapthydratiounsmethod iwwerginn an d'Wäerter vun de Pairen ginn op déi entspriechend Eegeschafte vun der Entitéit gesat.

"Schnell" (Präfix Fast a Benchmarks):

 protected override Contact GetContact(PropertyToValueCorrelation[] correlations)
        {
            var contact = new Contact();
            foreach (var setterMapItem in _proprtySettersMap)
            {
                var correlation = correlations.FirstOrDefault(x => x.PropertyName == setterMapItem.Key);
                setterMapItem.Value(contact, correlation?.Value);
            }
            return contact;
        }

Wéi mir kënne gesinn, gëtt eng statesch Sammlung mat Setter Eegeschafte benotzt - kompiléiert Lambdas déi d'Setter Entitéit nennen. Erstellt duerch de folgende Code:

        static FastContactHydrator()
        {
            var type = typeof(Contact);
            foreach (var property in type.GetProperties())
            {
                _proprtySettersMap[property.Name] = GetSetterAction(property);
            }
        }

        private static Action<Contact, string> GetSetterAction(PropertyInfo property)
        {
            var setterInfo = property.GetSetMethod();
            var paramValueOriginal = Expression.Parameter(property.PropertyType, "value");
            var paramEntity = Expression.Parameter(typeof(Contact), "entity");
            var setterExp = Expression.Call(paramEntity, setterInfo, paramValueOriginal).Reduce();
            
            var lambda = (Expression<Action<Contact, string>>)Expression.Lambda(setterExp, paramEntity, paramValueOriginal);

            return lambda.Compile();
        }

Am Allgemengen ass et kloer. Mir traverse d'Eegeschafte, kreéieren Delegéierte fir déi, déi Setzer uruffen, a späicheren se. Da ruffe mir wann néideg.

"Slow" (Präfix Slow a Benchmarks):

        protected override Contact GetContact(PropertyToValueCorrelation[] correlations)
        {
            var contact = new Contact();
            foreach (var property in _properties)
            {
                var correlation = correlations.FirstOrDefault(x => x.PropertyName == property.Name);
                if (correlation?.Value == null)
                    continue;

                property.SetValue(contact, correlation.Value);
            }
            return contact;
        }

Hei si mir direkt d'Eegeschafte ëmgoen an direkt SetValue ruffen.

Fir Kloerheet an als Referenz hunn ech eng naiv Method implementéiert déi d'Wäerter vun hire Korrelatiounsparen direkt an d'Entitéitsfelder schreift. Präfix - Manuell.

Loosst eis elo BenchmarkDotNet huelen an d'Performance ënnersichen. An op eemol ... (Spoiler - dëst ass net dat richtegt Resultat, Detailer sinn ënnert)

Mëssgléckt Artikel iwwer d'Beschleunegung vun der Reflexioun

Wat gesi mer hei? Methoden déi de Fast Präfix triumphant droen, ginn a bal all Passë méi lues wéi Methode mam Slow Präfix. Dëst ass wouer souwuel fir d'Allokatioun an d'Geschwindegkeet vun der Aarbecht. Op der anerer Säit, eng schéi an elegant Ëmsetzung vun der Kartéierung mat LINQ Methoden, déi dofir geduecht sinn, wou et méiglech ass, am Géigendeel, reduzéiert d'Produktivitéit staark. Den Ënnerscheed ass vun Uerdnung. Den Trend ännert sech net mat verschiddenen Zuelen vu Passë. Deen eenzegen Ënnerscheed ass an der Skala. Mat LINQ ass et 4 - 200 Mol méi lues, et gëtt méi Dreck op ongeféier déiselwecht Skala.

RTL

Ech hunn meng Ae net gegleeft, awer méi wichteg, eise Kolleg huet weder meng Aen nach mäi Code gegleeft - Dmitry Tikhonov 0x1000000. Nodeems ech meng Léisung duebel iwwerpréift hunn, huet hien brillant entdeckt an e Feeler drop higewisen, deen ech verpasst hunn wéinst enger Zuel vun Ännerungen an der Ëmsetzung, initial bis final. Nodeems Dir de fonnte Käfer am Moq Setup fixéiert huet, sinn all d'Resultater op der Plaz gefall. Laut den Retestresultater ännert sech den Haapttrend net - LINQ beaflosst nach ëmmer d'Performance méi wéi d'Reflexioun. Wéi och ëmmer, et ass flott datt d'Aarbecht mat der Expression-Kompilatioun net ëmsoss gemaach gëtt, an d'Resultat ass siichtbar souwuel an der Allokatioun an der Ausféierungszäit. Den éischte Start, wann statesch Felder initialiséiert ginn, ass natierlech méi lues fir déi "schnell" Method, awer dann ännert d'Situatioun.

Hei d'Resultat vum Retest:

Mëssgléckt Artikel iwwer d'Beschleunegung vun der Reflexioun

Fazit: wann Dir Reflexioun an enger Entreprise benotzt, ass et kee besonnesche Besoin fir Tricken ze benotzen - LINQ iessen d'Produktivitéit méi. Wéi och ëmmer, a High-load Methoden déi Optimisatioun erfuerderen, kënnt Dir Reflexioun a Form vun Initialisatoren an Delegéierte Compileren späicheren, déi dann "schnell" Logik ubidden. Op dës Manéier kënnt Dir souwuel d'Flexibilitéit vun der Reflexioun an d'Geschwindegkeet vun der Applikatioun erhalen.

De Benchmark Code ass hei verfügbar. Jiddereen kann meng Wierder duebel kontrolléieren:
HabraReflectionTests

PS: de Code an den Tester benotzt IoC, an an de Benchmarks benotzt en explizit Konstrukt. D'Tatsaach ass datt ech an der leschter Ëmsetzung all Faktoren ofgeschnidden hunn, déi d'Performance beaflosse kënnen an d'Resultat laut maachen.

PPS: Merci un de Benotzer Dmitry Tikhonov @0x1000000 fir mäi Feeler beim Opriichten vum Moq z'entdecken, wat déi éischt Miessunge beaflosst. Wann ee vun de Lieser genuch Karma huet, da gär et. De Mann ass gestoppt, de Mann huet gelies, de Mann huet duebel gepréift an de Feeler drop higewisen. Ech mengen dat ass de Respekt a Sympathie wäert.

PPPS: Merci un de virsiichtege Lieser, deen de Stil an den Design op de Buedem komm ass. Ech si fir Uniformitéit a Komfort. D'Diplomatie vun der Presentatioun léisst vill ze Wënsch, awer ech hunn d'Kritik berücksichtegt. Ech froe fir de Projektil.

Source: will.com

Setzt e Commentaire