Selgitan kohe artikli pealkirja. Alguses oli plaanis anda hĂ€id ja usaldusvÀÀrseid nĂ€punĂ€iteid refleksiooni kasutamise kiirendamiseks lihtsa, kuid realistliku nĂ€ite kaudu. Kuid mÔÔtmiste kĂ€igus selgus, et refleksioon ei ole nii aeglane, nagu ma arvasin; samas töötas LINQ aeglasemalt, kui olin oma öös unistustes kartnud. LĂ”ppkokkuvĂ”ttes selgus, et tegin ma mÔÔtmistes ka vea⊠TĂ€iendavad detailid sellest elulisest loost on all ja kommentaarides. Kuna nĂ€ide on piisavalt igapĂ€evane ja teostatud tavaliselt nii, nagu seda tehakse ettevĂ”ttes, on sellest kujunenud, vĂ€hemalt minu arvates, piisavalt huvitav elus.demo: peamise teema kiirusel ei olnud vĂ€listest loogikatest: Moq, Autofac, EF Core ja muust âvĂ€lisest sidumisestâ mĂ”ju mĂ€rgatav.
Alustasin tööd selle artikli muljetel:
NĂ€htavasti soovitab autor kasutada kompileeritud delegaatide asemel otse refleksioonitĂŒĂŒpide meetodite juurde pöördumist, mis on suurepĂ€rane viis rakenduse töö kiirus axe kiirendamiseks. Seal on loomulikult ka IL emissioon, kuid seda oleks soovitav vĂ€ltida, kuna see on kĂ”ige töömahukam ĂŒlesande tĂ€itmise viis, mis toob endaga kaasa vigu.
Arvestades, et olen alati sarnase arvamuse jaganud refleksiooni kiirusest, ei kavatsenud ma autori jÀreldusi eriti kahtluse alla seada.
Ma ei kohtunud harva naiivse refleksiooni kasutamisega ettevĂ”ttes. VĂ”etakse tĂŒĂŒp. VĂ”etakse teave omaduse kohta. Kutsutakse vĂ€lja meetod SetValue ja kĂ”ik on rÔÔmsad. VÀÀrtus jĂ”uab sihtvĂ€ljakule, kĂ”ik on rahul. Inimesed ei ole ĂŒldse lollid â vanemad ja tiimijuhid â kirjutavad oma laiendusi objektile, tuginedes sellisele naiivsele teostusele, tehes âuniversaalseidâ mappereid ĂŒhelt tĂŒĂŒbilt teisele. Tavaliselt on asi selles: vĂ”tame kĂ”ik vĂ€ljad, vĂ”tame kĂ”ik omadused, iteratsioonime nende ĂŒle: kui nimed klapitavad, siis tĂ€idame SetValue. Aeg-ajalt saame erandeid seal, kus ei leia mĂ”nda omadust ĂŒhelt tĂŒĂŒpidelt, kuid ka siin on lahendus, mis parandab jĂ”udlust. Try/catch.
Olen nĂ€inud, kuidas inimesed leiutavad parsereid ja mappereid, olles samas tĂ€iesti teadmatud, kuidas varem vĂ€lja mĂ”eldud rattaid töötab. Olen nĂ€inud, kuidas inimesed varjavad oma naiivseid rakendusi strateegiate, liideste ja sĂŒstimise taha, justkui see oleks vabandus jĂ€rgneva kaose jaoks. Sellest tĂŒĂŒpi rakendustest olen ma eemale hoidnud. Tegelikult ei ole ma tĂ”elist jĂ”udluse kadu mÔÔtnud ja vĂ”imalusel vahetasin lihtsalt rakenduse «optimaalsema» vastu, kui selleks aega leidsin. Seega, esimesed mÔÔtmised, millest allpool juttu, tĂ”id mind tĂ”siselt segadusse.
MÔtle, et paljud teist, lugedes Richterit vÔi teisi ideolooge, on silmitsi seisnud tÀitsa Ôigustatud vÀitega, et refleksioon koodis on nÀhtus, mis avaldab ÀÀrmiselt negatiivset mÔju rakenduse jÔudlusele.
Peegli kutsumine sunnib CLR-i kogusid uurima, vajalikud metadata tooma, neid analĂŒĂŒsima jne. Peale selle, peegeldamine jĂ€rjestuste lĂ€bimisel pĂ”hjustab suurt mĂ€lu kasutust. MĂ€lule kulutades, CLR vabastab GC-ja ja hakkavad tekkima viivitused. See on kindlasti aeglane, uskuge. Kaasaegsete tootmiserverite vĂ”i pilvemasinate suuremad mĂ€luressursid ei pÀÀsta teid kĂ”rgetest töötlemise viivitustest. Tegelikult, mida rohkem mĂ€lu, seda suurem on tĂ”enĂ€osus, et te mĂ€rkate, kuidas GC töötab. Peegeldamine on oma olemuselt liigne punane kalts selle jaoks.
TĂ”epoolest, me kĂ”ik kasutame nii IoC konteinerit kui ka andmete kaardistajaid, mille tööpĂ”himĂ”tted pĂ”hinevad samuti peegeldamisel, kuid nende jĂ”udluse osas ei esine tavaliselt kĂŒsimusi. Ei, mitte sellepĂ€rast, et sĂ”ltuvuste sĂŒstimine ja vĂ€liste piiratud konteksti mudelitest eristamine oleksid nii olulised asjad, et peaksime jĂ”udluse nimel igal juhul ohverdama. Asi on lihtsam â see tĂ”esti ei mĂ”juta jĂ”udlust oluliselt.
TĂ”siasi on see, et kĂ”ige levinumad raamistiku tehnoloogiad, mis pĂ”hinevad refleksioonil, kasutavad erinevaid nippide abil, et tagada efektiivsem töö. Ăldiselt kasutatakse vahemĂ€lu. TĂŒĂŒpiliselt on need vĂ€ljendid ja kompressitud vĂ€ljendite puu delegeeritud funktsioonid. Sama automaapper hoiab konkurentsivĂ”imelist sĂ”nastikku, mis seob tĂŒĂŒbid funktsioonidega, mis suudavad ĂŒksteisega ĂŒmber konverteerida juba ilma refleksiooni kutsumiseta.
Kuidas seda saavutatakse? PÔhimÔtteliselt ei erine see loogikast, mida platvorm ise kasutab JIT koodi genereerimiseks. Esimese vÀljakutse korral kompileeritakse meetod (jah, see protsess ei ole kiire), kuid jÀrgnevate vÀljakutsetega antakse juhtimine juba kompileeritud meetodile, kus erilisi jÔudluse langusi ei toimu.
Meie puhul saame kasutada JIT kompileerimist ja seejÀrel kasutada kompileeritud kÀitumist sama jÔudlusega nagu AOT analoogid. Expressions tulevad meile selles osas appi.
LĂŒhidalt vĂ”ib printsiipi, millest jutt, kokku vĂ”tta jĂ€rgmiselt:
Refleksiooni lĂ”pphaldust tuleks vahemĂ€lu hoida delegeeritud funktsiooni kujul, mis sisaldab kompileeritud funktsiooni. KĂ”ik vajalikud tĂŒĂŒbiinfo objektid tasuks samuti salvestada teie tĂŒĂŒbi vĂ€li â töötaja.
Selles on mĂ”te. Terve mĂ”istus ĂŒtleb meile, et kui midagi saab kompileerida ja vahemĂ€llu salvestada, siis tasub seda teha.
Kellestki ette pÀÀsemiseks tuleb öelda, et vahemĂ€lu kasutamisel refleksiooniga on oma eelised, isegi kui ei kasuta soovitatud vĂ€ljendite kompileerimise meetodit. Siinkohal kordaksin lihtsalt artikli autori teese, millele viitan ĂŒleval.
NĂŒĂŒd koodist. Vaatame nĂ€idet, mis pĂ”hineb minu hiljutisel valul, millega pidin tĂ”siselt tegelema tĂ”sises krediidiasutuses. KĂ”ik entiteedid on vĂ€ljamĂ”eldud, et keegi ei rikuks saladust.
On olemas mingisugune entiteet. Olgu see Contact. On kirjad, millel on standardiseeritud sisu, millest parsi ja hudrator loovad need kontaktid. Kiri saabus, loeme selle lÀbi, jagame vÔtme-vÀÀrtuse paarideks, loome kontakti ja salvestame andmebaasi.
See on elementaarne. Oletame, et kontaktis on omadused Nimi, Vanus ja kontakttelefon. Need andmed edastatakse ka kirjas. Samuti soovib Ă€ri, et tugiteenused saaksid kiiresti lisada uusi vĂ”tmeid, et kaardistada omadused paarid kirja kehas. Kui keegi tegi ĆĄabloonis trĂŒki- vĂ”i kui enne vĂ€ljalaskmist on vaja kiiresti algatada uus kaardistus uutelt partneritelt, kohandudes uue formaadiga. Siis saame uue kaardistuse korrelatsiooni lisada odava andmefiksina. See tĂ€hendab, et toome nĂ€ite elust.
Rakendame, loome teste. Toimib.
Koodi ma ei too: koode on saanud liiga palju ja need on saadaval GitHubis, lingi leiate artikli lĂ”pust. Saate neid alla laadida, masendavalt muuta ja mÔÔta, kuidas see teie puhul mĂ”juks. Toon vĂ€lja ainult kahe ĆĄabloonimeetodi koodi, mis eristavad kiiret hĂŒdratorit aeglasest hĂŒdratorist.
Loogika on jĂ€rgmine: mallimeetod saadab paare, mis on moodustatud parsi pĂ”hiloogikast. LINQ tase on parser ja hĂŒdreerija pĂ”hiloogika, mis kĂŒsib andmebaasi kontekstist ja vastab vĂ”tmed paaridele parserist (nende funktsioonide jaoks on olemas kood ilma LINQ-ita vĂ”rreldes). SeejĂ€rel edastatakse paarid peamisse hĂŒdreerimise meetodisse ja vÀÀrtused seadistatakse vastavatesse entiteedi omadustesse.
âKiireâ (Prefiks Fast tehingute testides):
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;
}
Nagu me nÀeme, kasutatakse staatilist kollektsiooni omaduste seadeid - kompileeritud lambdana, mis kutsuvad esile entiteedi seadiseid. 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 arusaadav. KĂ€ime lĂ€bi omadused, loome nende pĂ”hjal delegaadid, mis kutsuvad esile seadeid, salvestame. Siis kutsume vĂ€lja, kui on vaja.
âAeglaneâ (eelseisvas Slow mĂ€rk jaotustes):
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 lÀbi omadused ja kutsume otse vÀlja SetValue.
Selguse huvides ja vĂ”rdluseks rakendasin lihtsat meetodit, mis kirjutab paaride korrelatsiooni vÀÀrtused otse entiteetide vĂ€ljadele. Eesliide â Manual.
VĂ”tame nĂŒĂŒd BenchmarkDotNet'i ja uurime jĂ”udlust. Ja jĂ€rsku⊠(spoiler â see ei ole Ă”ige tulemus, ĂŒksikasjad â allpool)

Mida me siin nĂ€eme? Meetodid, kes uhkelt kannavad eesliidet Fast, on peaaegu kĂ”igil lĂ€bimistel aeglasemad kui meetodid, mille eesliide on Slow. See kehtib nii allokatsiooni kui ka töökiirusena. Teisalt, kena ja elegantne rakendus, mis kasutab kĂ”ikjal, kus vĂ”imalik, selleks mĂ”eldud LINQ meetodeid, sööb vastupidiselt jĂ”udlust tohutult. Erinevused on tohutud. Suundumus ei muutu erineva lĂ€bimise arvu korral. Erinevus on vaid mÔÔtkavas. LINQ puhul on 4 â 200 korda aeglasem, prĂŒgi on samuti umbes samas ulatuses rohkem.
UPDATEeritud
Ma ei saanud oma silmi uskuda, aga mis veelgi olulisem, meie kolleeg ei uskunud mitte ainult minu silmi, vaid ka minu koodi â . Kontrollides oma lahendust, tuvastas ta suurepĂ€raselt vea, mille ma mitmete muudatuste tĂ”ttu algversioonist lĂ”ppversioonini unustasin. PĂ€rast Moqi seadistuses leitud vea parandamist, olid kĂ”ik tulemused oma kohtadel. Uuesti testimise tulemused nĂ€itavad, et peamine suundumus ei muutu â LINQ mĂ”jutab jĂ”udlust endiselt tugevamini kui refleksioon. KĂŒll aga on hea kuulda, et avalduste kompileerimise töö ei lĂ€he raisku, ja tulemused on nĂ€htavad nii allokaatoris kui ka kĂ€itusaegades. Esimene kĂ€ivitamine, mil statilised vĂ€ljad initsialiseeritakse, on loogiliselt aeglasem 'kiire' meetodi puhul, kuid edaspidi olukord muutub.
Siin on uuesti testimise tulemus:

JĂ€reldus: ettevĂ”ttes refleksiooni kasutamisel pole erilisi trikke vaja â LINQ vĂ€hendab jĂ”udlust rohkem. Siiski, kĂ”rge koormusega meetodites, mis vajavad optimeerimist, vĂ”ib refleksiooni sĂ€ilitada initsialiseerijate ja delegaatide kompilaatorite kujul, mis tagavad hiljem 'kiire' loogika. Nii saate sĂ€ilitada nii refleksiooni paindlikkuse kui ka rakenduse töö kiirus.
Benchmarki kood on saadaval siin. KĂ”ik huvilised saavad mu sĂ”nu ĂŒle kontrollida:
PS: kooditestides kasutatakse IoC-d, samas kui benchmarkides on kasutusel selge konstruktsioon. LÔpplahenduses jÀtsin vÀlja kÔik tegurid, mis vÔiksid mÔjutada jÔudlust ja tulemusi hÀirida.
PPS: AitĂ€h kasutajale minu Moq seadistuse vea avastamise eest, mis mĂ”jutas esimesi mÔÔtmisi. Kui kellegil lugejatest on piisavalt karmaaktsiooni, palun andke talle like. Inimene tegi peatuse, luges sĂŒvenenult, kontrollis ĂŒle ja osutas veale. Arvan, et see vÀÀrib lugupidamist ja simpatiat.
PPPS: aitĂ€h sellele pĂ”hjalikule lugejale, kes uuris stiili ja vormindust. Olen ĂŒhtsuse ja mugavuse poolt. Esituse diplomaatilisus vĂ”iks olla parem, aga ma arvestasin kriitikaga. Palun, olge valmis.
Allikas: habr.com
