Eba artikkel refleksiooni kiirendamisest

Selgitame kohe artikli pealkirja. Alguses oli plaanis anda head ja usaldusvÀÀrset nĂ”u refleksiooni kasutamise kiirendamiseks lihtsa, kuid realistliku nĂ€ite abil. Siiski, benchmarkimise kĂ€igus selgus, et refleksioon ei toimi nii aeglaselt, nagu ma arvasin, ja LINQ töötab aeglasemalt, kui olin unes nĂ€inud. LĂ”puks selgus, et tegin mÔÔtmises ka vea... Selle eluloo yksikasjad on allpool ja kommentaarides. Kuna nĂ€ide on ĂŒsna igapĂ€evane ja teostatud pĂ”himĂ”tteliselt nagu tavaliselt ettevĂ”ttes, on tulemuseks huvitav, nagu mulle tundub, elu demonstreerimine: peamise artikli teema kiirusenĂ€itaja ei olnud mĂ€rgatav vĂ€listest loogikatest: Moq, Autofac, EF Core ja muu â€žĂŒmberringi“.

Hakkasin tööd tegema selle artikli muljetest: Miks on refleksioon aeglane

Nagu nĂ€ha, pakub autor kasutama kompileeritud delegaate otse refleksioonitĂŒĂŒbi meetodite kĂ€sitlemise asemel, mis on suurepĂ€rane viis rakenduse töö kiirusel oluliselt kiirendamiseks. Seal on veel IL emissioon, kuid sellest tahaksin vĂ€ltida, kuna see on kĂ”ige töömahukam viis ĂŒlesande tĂ€itmiseks, mis vĂ”ib tuua kaasa vigu.

Arvestades, et olen alati arvanud, et refleksiooni kiirus on sarnane, ei kavatse ma autori jÀreldusi kahtluse alla seada.

Kohtan tihti naiivset refleksiooni kasutamist ettevĂ”ttes. VĂ”etakse tĂŒĂŒp. Saadakse teave omadusest. Kutsutakse vĂ€lja meetod SetValue ja kĂ”ik rÔÔmustavad. VÀÀrtus jĂ”udis sihitud vĂ€lja, kĂ”ik on rahul. Inimesed on ĂŒsna arukad — vanemad ja tiimijuhid — kirjutavad oma laiendusi object pĂ”hiselt, tuginedes sellisele naivsele rakendusele â€žĂŒldine” mappimine ĂŒhelt tĂŒĂŒbilt teisele. Sisuliselt on see tavaliselt selline: vĂ”tame kĂ”ik vĂ€ljad, vĂ”tame kĂ”ik omadused, iteratsiooni need: nimesid siiski ĂŒhtivad liikmed tĂŒĂŒbist, teeme SetValue. Aeg-ajalt pĂŒĂŒame erandeid seal, kus ei suudetud leida mĂ”nda omadust ĂŒksikutest tĂŒĂŒpidest, kuid siin on ka vĂ€ljapÀÀs, mis parandab jĂ”udlust. Try/catch.

Olen nĂ€inud, kuidas inimesed leiavad korduvkasutamiseks parsereid ja kaardistajaid, ilma et nad oleksid tĂ€ielikult relvastatud teadmistega, kuidas varem leiutatud jalgrattad töötavad. Olen nĂ€inud, kuidas inimesed peidavad oma naiivseid teostusi strateegiate, liideste ja sĂŒstide taha, justkui see Ă”igustaks jĂ€rgnevat segadust. Selliste teostuste pĂ€rast keerutasin nina. Tegelikult ei ole ma tĂ”elist jĂ”udluse lekkimist mÔÔtnud ja vĂ”imaluse korral lihtsalt vahetasin teostust „optimaalsema” vastu, kui oli aega. SeetĂ”ttu ajasid mulle allpool mainitud esimesed mÔÔtmised tĂ”siselt segadusse.

Arvan, et paljud teist, lugedes Richterit vÔi teisi ideoloogiaid, on kokku puutunud tÀiesti Ôigustatud vÀitega, et refleksioon koodis on nÀhtus, mis avaldab ÀÀrmiselt negatiivset mÔju rakenduse jÔudlusele.

Refleksioonikutsed sunnivad CLR-i kÀima lÀbi kogusid otsides vajalikke, tÔmbama vÀlja nende metaandmed, parsima neid jne. Peale selle pÔhjustab refleksioon jÀrjestuste lÀbimisel suure hulga mÀlu eraldamise. Me kulutame mÀlu, CLR vabastab GC ja algavad hÀired. See peaks olema mÀrgatavalt aeglane, uskuge. Kaasaegsete tootmisserverite vÔi pilve masinate tohutu mÀlu ei pÀÀsta kÔrgete viivituste eest töötlemisel. Tegelikult, mida rohkem mÀlu, seda suurem on tÔenÀosus, et te mÀrkate, kuidas GC töötab. Refleksioon on ideeliselt tarbetu punane lipp tema jaoks.

Kuid me kĂ”ik kasutame nii IoC konteinerite kui ka andmete kaardistajaid, mille tööpĂ”himĂ”te pĂ”hineb samuti refleksioonil, kuid nende jĂ”udluse suhtes ei esita tavaliselt kĂŒsimusi. Ei, mitte sellepĂ€rast, et sĂ”ltuvuste sisestamine ja mudelitest vĂ€lise piiratud konteksti abstraheerimine oleksid nii vajalikud asjad, et peame jĂ”udluse nimel igal juhul ohverdama. KĂ”ik on lihtsam – see ei mĂ”juta tĂ”eliselt jĂ”udlust.

Asi on selles, et kĂ”ige levinumad raamistikud, mis pĂ”hinevad refleksioonitehnoloogial, kasutavad igasuguseid nikerdusi, et paremini sellega töötada. Tavaliselt on see vahemĂ€lu. Reeglina – see on Expressions ja vĂ€ljendite puust kompileeritud delegaadid. Sama automaapper hoiab enda all konkurentsi sĂ”naraamatut, mis seob tĂŒĂŒbid funktsioonidega, mis saavad ĂŒksteisele konverteerida juba ilma refleksioonikutsumata.

Kuidas see saavutatakse? PÔhimÔtteliselt ei erine see sellest loogikast, mida platvorm ise JIT-koodi genereerimiseks kasutab. Esimese meetodi vÀljakutse korral kompileeritakse see (ja jah, see protsess ei ole kiire), edasiste vÀljakutsete korral antakse juhtimine juba kompileeritud meetodile, ja siin ei teki erilisi jÔudluse langusi.

Meie puhul saab samuti kasutada JIT kompileerimist ja seejÀrel kasutada kompileeritud kÀitumist sama jÔudlusega nagu AOT-analoogid. Abiks on meil antud juhul vÀljendid.

LĂŒhidalt vĂ”ib esitada printe, millest rÀÀgitakse, jĂ€rgmiselt:
Tuleb vahemĂ€lestada refleksiooni töö lĂ”pptulemus delegaadina, mis sisaldab kompileeritud funktsiooni. KĂ”ik vajalikud objektid tĂŒĂŒbi info kohta on samuti mĂ”istlik vahemĂ€lestada teie tĂŒĂŒbi – töötaja – objekti vĂ€li.

Selles on pĂ”hjus. Terve mĂ”istus ĂŒtleb meile, et kui midagi on vĂ”imalik kompileerida ja vahemĂ€llu salvestada, siis peaksime seda tegema.

Edasi minnes tuleb öelda, et vahemÀlu refleksiooniga töötamisel on oma eelised, isegi kui ei kasuta pakutud vÀljenduskompileerimise meetodit. Tegelikult kordan siin autori artikli teemasid, millele viidatud.

NĂŒĂŒd koodist. Vaatame nĂ€idet, mis pĂ”hineb minu hiljutisel murel, millega pidin tegelema tĂ”sises tootmises tĂ”sises krediidiorganisatsioonis. KĂ”ik entiteedid on vĂ€ljamĂ”eldud, et keegi ei arvaks.

On mingi entiteet. Olgu selleks Kontakt. On kirjad standardiseeritud sisuga, millest parser ja hydrator loovad need kontaktid. Kirjaga, mis saabuva, loeme selle, jagame paarideks vÔtme-vÀÀrtuse, loome kontakti ja salvestame andmebaasi.

See on elementaarne. Oletame, et kontaktis on omadused Nimi, Vanus ja kontakttelefon. Need andmed saadetakse kirjas. Samuti soovib Àri, et tugiteenuste töötajad saaksid kiiresti lisada uusi vÔtmeid entiteedi omaduste kaardistamiseks kirja kehas. Juhuks, kui keegi on malli valesti kirjutanud vÔi kui enne vÀljalaskmist tuleb kiiresti stardida uue partneri kaardistus, kohandades uut formaati. Siis saame uue kaardistuskorrelatsiooni lisada kui odava andmefikse. Seega, eluline nÀide.

Rakendame ja loome teste. Töötab.

Ma ei too siia koodi: lĂ€htekoodi on liiga palju ja need on saadaval GitHubis artikli lĂ”pus antud lingil. Saate neid alla laadida, nad vĂ”ivad olla teid Ă€ra tĂŒĂŒtavad ja mÔÔta, kuidas see teie puhul mĂ”juks. Toon vĂ€lja ainult kahe mallimeetodi koodi, millega erinevad kiire ja aeglane hĂŒdraator.

Loogika on selline: mallimeetod saab paare, mis on moodustatud parseri pĂ”hiloogikast. LINQ tase on parser ja hĂŒdraatori pĂ”hiline loogika, mis teeb pĂ€ringut andmebaasi konteksti ja seondab vĂ”tmed parseri paaridega (nende funktsioonide jaoks on kood ilma LINQ-ita vĂ”rdlemiseks). SeejĂ€rel edastatakse paarid pĂ”hihĂŒdreerimise meetodisse ning vÀÀrtused mÀÀratakse asjakohaste entiteedi omaduste jĂ€rgi.

„Kiire” (prefiks Fast tulemustes):

 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;
        }

Kuidas nĂ€eme, kasutatakse staatilist kogumit omaduste seadmiseks – kompileeritud lambda-funktsioone, mis kutsuvad esile entiteedi seadjat. Need luuakse jĂ€rgmise koodiga:

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

        private static Action 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>)Expression.Lambda(setterExp, paramEntity, paramValueOriginal);

            return lambda.Compile();
        }

Üldiselt on see selge. LĂ€bime omadused, loome nendest delegeeritud funktsioonid, mis kutsuvad esile seadjad, salvestame. SeejĂ€rel kutsume neid vĂ€lja, kui on vaja.

„Aeglase” (prefiks Slow tulemustes):

        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;
        }

Siin kÀime kohe omadused lÀbi ja kutsume otse SetValue esile.

Kujutamiseks ja mÔÔdupuu rollis olen rakendanud naiivset meetodit, mis kirjutab paaride korrelatsiooni vÀÀrtused otse entiteetide vĂ€ljadele. Eesliide – Manual.

NĂŒĂŒd vĂ”tame BenchmarkDotNet'i ja uurime jĂ”udlust. Ja Ă€kki... (spoiler – see ei ole Ă”ige tulemus, ĂŒksikasjad allpool)

Eba artikkel refleksiooni kiirendamisest

Mida me siin nĂ€eme? Meetodid, mis uhkelt kannavad eesliidet Fast, on peaaegu kĂ”ikides lĂ€bimistest aeglasemad kui meetodid, millel on eesliide Slow. See kehtib nii allokatsiooni kui ka töökiirusel. Teisest kĂŒljest, kaunis ja elegantne mapimise rakendamine, kus igal pool, kus vĂ”imalik, kasutatakse selleks ette nĂ€htud LINQ meetodeid, vastupidi, tarbib oluliselt rohkem jĂ”udlust. Erinevus on jĂ€rsk. Suundumus ei muutu erineva lĂ€bimiste arvuga. Erinevus on vaid mÔÔtmetes. LINQ puhul on see 4 – 200 korda aeglasem, ja prĂŒgikogu on umbes sama suurtes mÔÔtmetes.

Uuendatud

Ma ei uskunud oma silmi, kuid mis veelgi olulisem, ei uskunud meie kolleeg mitte ainult minu silmi, vaid ka minu koodi — Dmitry Tikhonov 0x1000000. Kordades minu lahendust leidis ja osutas ta suurepĂ€raselt veale, mille ma seoses mitmete muudatustega rakenduses algsest lĂ”puni olin unustanud. PĂ€rast leitud vea parandamist Moqi seadistuses, seadsid kĂ”ik tulemused end Ă”igesse kohta. Kordustestimise tulemuste pĂ”hjal ei muutu peamine suundumus — LINQ mĂ”jutab jĂ”udlust endiselt tugevamalt kui refleksioon. Kuid on meeldiv, et töö vĂ€ljexpressiivsete vĂ€ljendite kompileerimisega ei ole olnud asjatu, ja tulemus on nĂ€htav nii allokatsioonis kui ka tĂ€itmise ajas. Esimene kĂ€ivitamine, kui staatilisi vĂ€lju initsialiseeritakse, on loomulikult „kiire“ meetodi puhul aeglasem, kuid hiljem olukord muutub.

Siin on kordustestimise tulemus:

Eba artikkel refleksiooni kiirendamisest

KokkuvĂ”tteks: ettevĂ”ttes refleksiooni kasutamisel ei ole erilisi nikerdusi vaja — LINQ neelab jĂ”udlust palju rohkem. Siiski vĂ”ib kĂ”rge koormuse meetodites, mis vajavad optimeerimist, hoida refleksiooni initsialiseerijate ja delegaatide kompilaatorite kujul, mis hiljem tagavad „kiire“ loogika. Nii saate sĂ€ilitada nii refleksiooni paindlikkuse kui ka rakenduse töö kiirus.

Kood koos bĂ€nhcmariga on siin saadaval. KĂ”ik huvilised saavad mu sĂ”nu ĂŒmber kontrollida:
HabraReflectionTests

PS: testides kasutatav kood kasutab IoC-d, samas kui benchmarkides – selget konstruktsiooni. Asi on selles, et lĂ”ppversioonis ma kĂ”rvaldasin kĂ”ik tegurid, mis vĂ”iksid mĂ”jutada jĂ”udlust ja tulemuse mĂŒrastada.

PPS: AitĂ€h kasutajale Dmitry Tikhonov @0x1000000 , et avastas minu vea Moqi seadistuses, mis mĂ”jutas esimesi mÔÔtmisi. Kui mĂ”nel lugejal on piisavalt karmat, siis palun nagu tema. Inimene peatus, luges sĂŒvenenult, kontrollis ja osutas veale. Ma arvan, et see on austust ja sĂŒmpaatilisust vÀÀriv.

PPPS: aitÀh sellele detailidele tÀhelepanelikule lugejale, kes uuris stiili ja vormingut. Ma olen jÀrjepidevuse ja mugavuse poolt. Esituse diplomaatilisus jÀtab soovida, kuid ma arvestasin kriitikaga. Palun tulge siia.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster