Tugoti ako nga ipasabut dayon ang titulo sa artikulo. Sa sinugdanan, nagplano ako nga maghatag ug maayo ug kasaligan nga tambag sa pagpadali sa pagpamalandong gamit ang usa ka yano apan realistiko nga pananglitan. Bisan pa, atol sa benchmarking, nahimo nga ang pagpamalandong dili ingon ka hinay sa akong gihunahuna, ug ang LINQ mas hinay kaysa akong gihunahuna. Ug nahimo usab nga nakahimo ako usa ka sayup sa pagsukod... Ang mga detalye niining tinuod nga istorya sa kinabuhi anaa sa ubos sa giputol ug sa mga komento. Tungod kay ang pananglitan medyo ordinaryo ug gipatuman sa usa ka paagi nga kasagaran gihimo sa negosyo, nahimo kini nga usa ka makapaikag nga demonstrasyon, sa akong opinyon: ang epekto sa performance sa pangunang hilisgutan sa artikulo wala mamatikdi tungod sa eksternal nga lohika: Moq, Autofac, EF Core, ug uban pang "mga binding."
Nagsugod ko sa pagtrabaho ubos sa impresyon niini nga artikulo:
Sama sa imong makita, gisugyot sa tagsulat ang paggamit sa mga compiled delegates imbes nga direktang mo-access sa mga reflection type methods isip usa ka maayong paagi aron mapadali pag-ayo ang performance sa aplikasyon. Siyempre, naa pud ang IL emission, apan mas maayo nga likayan kini, tungod kay kini ang pinaka-hago ug dali nga masayop nga paagi aron matuman ang buluhaton.
Tungod kay kanunay ko nga adunay parehas nga opinyon bahin sa katulin sa pagpamalandong, wala ko tuyoa nga kwestyonon ang mga konklusyon sa tagsulat.
Kanunay kong makasugat og mga inosenteng gamit sa reflection sa negosyo. Usa ka type ang gikuha. Ang impormasyon sa property ang gikuha. Ang SetValue method gitawag, ug ang tanan malipayon. Ang value natugpa na sa target field, ang tanan malipayon. Ang mga maalamonāmga senior ug team leadānagsulat sa ilang kaugalingong mga extension sa object, nga gibase ang ilang "universal" mappers gikan sa usa ka type ngadto sa lain niining inosenteng implementasyon. Ang punto kasagaran mao kini: kuhaa ang tanang field, kuhaa ang tanang property, ug i-iterate kini: kon ang mga ngalan sa miyembro sa type motakdo, among i-execute ang SetValue. Matag karon ug unya, among madakpan ang mga exception kon ang usa ka property wala makit-i alang sa usa sa mga type, apan bisan dinhi adunay paagi aron mapaayo ang performance: Try/catch.
Nakakita na kog mga tawo nga nag-imbento pag-usab og mga parser ug mapper nga wala hingpit nga nahibalo kon giunsa pagtrabaho ang mga wheel nga naimbento kaniadto. Nakakita na kog mga tawo nga nagtago sa ilang inosenteng mga implementasyon luyo sa mga estratehiya, interface, ug mga injection, nga daw kana ang hinungdan sa sunod nga kagubot. Wala ko mouyon sa ingon nga mga implementasyon. Sa tinuod lang, wala nako sukda ang aktuwal nga pagkahurot sa performance, ug kon mahimo, giilisan lang nako ang implementasyon og mas "optimal" kon naa koy panahon. Mao nga ang inisyal nga mga sukod, nga gihisgutan sa ubos, nakapalibog gyud nako.
Sa akong hunahuna daghan kaninyo, samtang nagbasa sa mga sinulat ni Richter o sa ubang mga ideologo, ang nakakaplag sa hingpit nga makatarunganon nga pangangkon nga ang pagpamalandong sa code usa ka panghitabo nga adunay negatibo kaayo nga epekto sa performance sa usa ka aplikasyon.
Ang pagtawag sa reflection mopugos sa CLR sa pag-traverse sa mga assembly aron makit-an ang husto, kuhaon ang ilang metadata, i-parse kini, ug uban pa. Dugang pa, ang reflection atol sa sequence traversal mosangpot sa dakong memory allocations. Atong gamiton ang memory, ang CLR mo-unpack sa GC, ug mo-freeze. Kini kinahanglan nga mamatikdan nga hinay, tuohi ko. Ang dakong kantidad sa memory sa modernong production servers o cloud machines dili makapugong sa taas nga processing latencies. Sa tinuod lang, kon mas daghan ang imong memory, mas taas ang posibilidad nga imong MAMATI ang performance sa GC. Ang reflection, sa teorya, usa ka dili kinahanglan nga red flag alang niini.
Bisan pa man, kitang tanan naggamit sa IoC containers ug data mappers, nga nagsalig usab sa reflection, apan ang ilang performance kasagaran dili maapektuhan. Dili tungod kay ang dependency injection ug abstraction gikan sa external bounded context models gikinahanglan kaayo nga kinahanglan natong isakripisyo ang performance sa bisan unsang kaso. Mas simple kiniādili gyud kini dako og epekto sa performance.
Ang tinuod mao nga ang labing komon nga mga framework nga gibase sa reflection naggamit sa tanang matang sa mga limbong aron ma-optimize ang paggamit niini. Kasagaran, kini naglambigit sa usa ka cache. Ang mga ekspresyon ug mga delegado nga gi-compile gikan sa mga expression tree komon usab. Ang automapper, pananglitan, nagmintinar sa usa ka concurrent dictionary nga nagmapa sa mga tipo ngadto sa mga function nga mahimong i-convert sa usag usa nga dili mogamit sa reflection.
Giunsa kini pagkab-ot? Sa laktod nga pagkasulti, wala kini kalainan sa lohika nga gigamit sa plataporma mismo sa pagmugna og JIT code. Sa unang higayon nga gitawag ang usa ka pamaagi, kini gi-compile (ug, oo, kini nga proseso dili paspas). Ang sunod nga mga tawag nagbalhin sa kontrol ngadto sa gi-compile nga pamaagi, ug walay bisan unsang dakong epekto sa performance.
Sa atong kaso, mahimo usab natong pahimuslan ang JIT compilation ug dayon gamiton ang na-compile nga behavior nga adunay parehas nga performance sama sa mga katugbang niini sa AOT. Ang mga expression ang makatabang niini nga kaso.
Ang prinsipyo nga gihisgutan mahimong mubo nga maporma sama sa mosunod:
Ang katapusang resulta sa reflection kinahanglan nga i-cache isip usa ka delegado nga adunay sulod nga na-compile nga function. Mas maayo usab nga i-cache ang tanan nga kinahanglanon nga mga butang nga adunay impormasyon sa tipo sa mga field sa imong worker type nga gitipigan sa gawas.
Naa gyuy lohika ani. Ang sentido komon nagsulti nato nga kon ang usa ka butang mahimong i-compile ug i-cache, kinahanglan gyud nga mahimo kini.
Sa paglantaw sa unahan, angayng matikdan nga ang caching adunay mga bentaha kon mogamit og reflection, bisan kon dili gamiton ang gisugyot nga pamaagi sa pag-compile sa expression. Sa tinuod lang, gisubli lang nako dinhi ang mga punto sa tagsulat sa artikulo nga akong gi-link sa ibabaw.
Karon, atong tan-awon ang code. Atong tan-awon ang usa ka ehemplo base sa usa ka bag-o nga problema nga akong nasinati sa usa ka dakong production environment sa usa ka dakong financial institution. Ang tanang entities hinimo-himo lang, busa walay makatag-an.
Naa'y usa ka entidad. Tawgon nato kini nga Contact. Naa'y mga email nga adunay standardized nga mga lawas, diin ang parser ug hydrator ang mohimo niining mga kontak. Moabot ang email, basahon nato kini, bahinon kini ngadto sa mga key-value pairs, maghimo og contact, ug i-save kini sa database.
Simple ra. Ingnon ta nga ang usa ka kontak adunay mga kabtangan sama sa tibuok nga ngalan, edad, ug numero sa telepono. Kini nga datos ipadala sa email. Ang negosyo gusto usab og suporta aron dali nga makadugang og bag-ong mga yawe para sa pagmapa sa mga kabtangan sa entidad ngadto sa mga pares sa lawas sa email. Kini kung adunay makahimo og typo sa template o kung ang pagmapa kinahanglan nga ilunsad dayon gikan sa usa ka bag-ong partner sa dili pa ang pagpagawas, nga mopahiangay sa usa ka bag-ong format. Dayon makadugang kita og bag-ong mapping correlation isip barato nga pag-ayo sa datos. Mao nga, kini usa ka tinuod nga ehemplo.
Among gipatuman kini, naghimo og mga pagsulay. Molihok kini.
Dili nako ipakita ang code: daghan kaayong source code, ug kini anaa sa GitHub pinaagi sa link sa katapusan sa artikulo. Mahimo nimo kini i-download, i-tweak kini nga dili mailhan, ug sukdon kung giunsa kini makaapekto sa imong kaso. Ipakita lang nako ang code para sa duha ka template methods nga nagpalahi sa hydrator nga unta paspas gikan sa hydrator nga unta hinay.
Ang lohika mao ang mosunod: ang template method makadawat og mga pares nga gihimo sa core logic sa parser. Ang LINQ layer mao ang core logic sa parser ug hydrator, nga nag-query sa database context ug nagpares sa mga key sa mga pares sa parser (adunay non-LINQ code para niining mga function para sa pagtandi). Ang mga pares dayon ipasa ngadto sa main hydration method, ug ang mga pair value ibutang sa katugbang nga entity properties.
"Paspas" (Unang Paspas sa mga benchmark):
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;
}
Sama sa atong makita, usa ka static collection nga adunay mga property setter ang gigamitāmga lambda nga gi-compile nga nagtawag sa setter sa entity. Kini gihimo gamit ang mosunod nga 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();
}
Kasagaran klaro. Atong susihon ang mga kabtangan, maghimo og mga delegado para niini, motawag sa mga setter, ug mo-save niini. Dayon atong tawagon sila kon gikinahanglan.
"Hinay" (Unapang hinay sa mga benchmark):
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;
}
Dinhi atong laktawan dayon ang mga kabtangan ug direktang tawagon ang SetValue.
Para sa katin-awan ug isip sukdanan, nagpatuman ko og usa ka yano nga pamaagi nga nagsulat sa mga bili sa ilang mga pares sa korelasyon direkta sa mga natad sa entidad. Ang prefix kay Manwal.
Karon atong kuhaon ang BenchmarkDotNet ug sulayan ang performance niini. Ug kalit lang... (spoiler alert: dili kini sakto nga resulta; ang mga detalye naa sa ubos)

Unsay atong makita dinhi? Ang mga pamaagi nga nagmadaugon nga nagdala sa Fast prefix mas hinay kay sa mga pamaagi nga adunay Slow prefix sa halos tanang mga pass. Kini magamit sa parehong alokasyon ug katulin sa pagpatuman. Sa laing bahin, ang usa ka matahum ug elegante nga pagpatuman sa mapping, nga naggamit sa mga pamaagi sa LINQ kung mahimo, makapakunhod pag-ayo sa performance. Ang kalainan dako kaayo. Kini nga uso dili mausab sa lainlaing gidaghanon sa mga pass. Ang bugtong kalainan mao ang sukod. Sa LINQ, kini 4-200 ka pilo nga mas hinay, nga adunay halos parehas nga gidaghanon sa basura.
Updated
Dili ko makatuo sa akong nakita, apan mas importante, ang among kauban wala mituo sa akong nakita o sa akong kodigo - Human sa pagsulay pag-usab sa akong solusyon, maayo kaayo niyang nakita ug gipunting ang usa ka sayop nga akong nasipyat tungod sa daghang mga pagbag-o sa inisyal nga implementasyon. Human sa pag-ayo sa nadiskobrehan nga bug sa Moq setup, ang tanan nga mga resulta mibalik sa normal. Ang mga resulta sa pagsulay pag-usab nagpakita nga ang pangunang uso nagpabilin nga wala mausabāang LINQ mas makaapekto gihapon sa performance kaysa sa reflection. Bisan pa, nindot nga makita nga ang trabaho sa pag-compile sa mga Expressions mapuslanon, ug ang mga resulta makita sa oras sa alokasyon ug pagpatuman. Ang unang pagdagan, kung ang mga static field gi-initialize, natural nga mas hinay alang sa "paspas" nga pamaagi, apan unya mausab ang sitwasyon.
Ania ang resulta sa pag-usab sa pagsulay:

Konklusyon: Kon mogamit og reflection sa enterprise, dili na kinahanglan nga mogamit og mga limbongāang LINQ mokunhod pag-ayo sa performance. Apan, sa mga high-load nga pamaagi nga nanginahanglan og optimization, ang reflection mahimong mapreserbar sa porma sa mga initializer ug delegate compiler, nga mohatag unya og "paspas" nga lohika. Niining paagiha, mapreserbar nimo ang pagka-flexible sa reflection ug ang katulin sa imong aplikasyon.
Ang benchmark code makita dinhi. Bisan kinsa nga interesado mahimong mo-double check sa akong mga statement:
PS: Ang code sa mga pagsulay naggamit ug IoC, samtang ang mga benchmark naggamit ug klaro nga construct. Kini tungod kay sa katapusang implementasyon, akong gitangtang ang tanang mga butang nga makaapekto sa performance ug makabalda sa mga resulta.
PPS: Salamat sa tiggamit Sa pag-ila sa akong sayop sa Moq setup, nga nakaapekto sa inisyal nga mga sukod. Kon adunay magbabasa nga adunay igo nga karma, palihug i-like. Adunay mihunong, adunay mibasa pag-ayo, adunay mi-double check, ug gitudlo ang sayop. Sa akong hunahuna kini angay nga respeto ug simpatiya.
PPPS: Salamat sa maampingong magbabasa nga misusi pag-ayo sa estilo ug layout. Uyon ko sa makanunayon ug sayon āānga pagkasulat. Daghan pa ang kulang sa diplomasya sa presentasyon, apan gikonsiderar nako ang mga pagsaway. Palihug pagsugod na sa pagtrabaho.
Source: www.habr.com
