Artikel sing ora kasil babagan nyepetake refleksi

Aku bakal langsung nerangake judhul artikel. Rencana asli yaiku menehi saran sing apik lan dipercaya babagan carane nyepetake panggunaan refleksi nggunakake conto sing prasaja nanging nyata, nanging sajrone benchmarking ternyata refleksi ora alon kaya sing dakkira, LINQ luwih alon tinimbang ing ngimpi elek. Nanging ing pungkasan ternyata aku uga salah ing pangukuran ... Rincian crita urip iki ana ing ngisor iki lan ing komentar. Wiwit conto iki cukup umum lan dileksanakake ing prinsip kaya biasane ditindakake ing perusahaan, mula dadi menarik, kaya sing dakkarepake, demonstrasi urip: pengaruhe ing kacepetan subyek utama artikel kasebut. ora katon amarga logika external: Moq, Autofac, EF inti lan liyane "bandings".

Aku wiwit kerja miturut kesan artikel iki: Kenapa Refleksi alon

Kaya sing sampeyan ngerteni, panulis nyaranake nggunakake delegasi kompilasi tinimbang langsung nelpon metode jinis refleksi minangka cara sing apik kanggo nyepetake aplikasi kasebut. Mesthi ana emisi IL, nanging aku pengin nyingkiri, amarga iki minangka cara sing paling intensif kanggo nindakake tugas kasebut, sing ana kesalahan.

Ngelingi yen aku tansah duwe pendapat sing padha babagan kacepetan refleksi, aku ora pengin takon babagan kesimpulan penulis.

Aku kerep nemoni panggunaan refleksi sing naif ing perusahaan. Jinis dijupuk. Informasi babagan properti dijupuk. Metode SetValue diarani lan kabeh wong padha bungah. Nilai wis teka ing lapangan target, kabeh padha seneng. Wong sing pinter banget - senior lan pimpinan tim - nulis ekstensi menyang obyek, adhedhasar implementasine naif "universal" pemetaan saka siji jinis menyang liyane. Intine biasane iki: kita njupuk kabeh lapangan, njupuk kabeh sifat, iterate liwat: yen jeneng anggota jinis cocog, kita nglakokaké SetValue. Saka wektu kanggo wektu kita nyekel pangecualian amarga kesalahane ngendi kita ora nemu sawetara property ing salah siji saka jinis, nanging malah kene ana cara metu sing nambah kinerja. Coba / nyekel.

Aku wis weruh wong reinvent parser lan mappers tanpa lengkap bersenjata karo informasi bab carane mesin sing teka sadurunge padha bisa digunakake. Aku wis ndeleng wong ndhelikake implementasine naif konco Sastranegara, konco antarmuka, konco injeksi, minangka yen iki bakal alesan bacchanalia sakteruse. Aku nguripake irung munggah ing realizations kuwi. Nyatane, aku ora ngukur kebocoran kinerja sing nyata, lan, yen bisa, aku mung ngganti implementasine dadi luwih "optimal" yen bisa entuk tanganku. Mulane, pangukuran pisanan sing dibahas ing ngisor iki saya bingung.

Aku akeh sing, maca Richter utawa ideologists liyane, wis teka tengen statement rampung adil sing bayangan ing kode punika kedadean sing wis impact banget negatif ing kinerja aplikasi.

Refleksi telpon meksa CLR ngliwati majelis kanggo nemokake sing dibutuhake, narik metadata, ngurai, lsp. Kajaba iku, refleksi nalika ngliwati urutan ndadékaké alokasi memori sing akeh. We nggunakake munggah memori, CLR miyak GC lan friezes miwiti. Iku kudu noticeably alon, pracaya kula. Jumlah gedhe saka memori ing server produksi modern utawa mesin maya ora nyegah wektu tundha Processing dhuwur. Nyatane, luwih akeh memori, luwih akeh sampeyan bakal nggatekake cara kerjane GC. Refleksi, ing teori, kain abang ekstra kanggo dheweke.

Nanging, kita kabeh nggunakake kontaner IoC lan peta tanggal, prinsip operasi uga adhedhasar bayangan, nanging biasane ora ana pitakonan babagan kinerja. Ora, ora amarga introduksi dependensi lan abstraksi saka model konteks winates eksternal perlu banget supaya kita kudu ngorbanake kinerja ing kasus apa wae. Kabeh luwih prasaja - pancen ora mengaruhi kinerja.

Kasunyatane yaiku kerangka kerja sing paling umum adhedhasar teknologi refleksi nggunakake macem-macem trik supaya bisa digunakake kanthi luwih optimal. Biasane iki cache. Biasane iki Ekspresi lan delegasi sing disusun saka wit ekspresi. Automapper sing padha njaga kamus kompetitif sing cocog karo jinis kanthi fungsi sing bisa ngowahi siji liyane tanpa nelpon refleksi.

Kepiye carane iki bisa ditindakake? Intine, iki ora beda karo logika sing digunakake platform kasebut kanggo ngasilake kode JIT. Nalika cara disebut pisanan, iku dikompilasi (lan, ya, proses iki ora cepet); ing telpon sakteruse, kontrol ditransfer menyang cara wis nyawiji, lan ora bakal ana drawdowns kinerja sing signifikan.

Ing kasus kita, sampeyan uga bisa nggunakake kompilasi JIT banjur nggunakake prilaku kompilasi kanthi kinerja sing padha karo mitra AOT. Ekspresi bakal mbantu kita ing kasus iki.

Prinsip kasebut bisa dirumusake kanthi ringkes kaya ing ngisor iki:
Sampeyan kudu nyimpen asil pungkasan refleksi minangka utusan sing ngemot fungsi sing dikompilasi. Iku uga ndadekake pangertèn kanggo cache kabeh obyek perlu karo informasi jinis ing kothak saka jinis, buruh, sing disimpen ing njaba obyek.

Ana logika ing iki. Akal sehat ngandhani yen ana sing bisa disusun lan di-cache, mula kudu ditindakake.

Looking ahead, iku kudu ngandika sing cache ing karya karo bayangan duwe kaluwihan, malah yen sampeyan ora nggunakake cara ngajokaken kanggo kompilasi ungkapan. Sejatine, ing kene aku mung mbaleni tesis saka penulis artikel sing dakkandhakake ing ndhuwur.

Saiki babagan kode. Ayo goleki conto sing adhedhasar rasa lara anyar sing kudu dakdeleng ing produksi serius institusi kredit serius. Kabeh entitas iku fiktif supaya ora ana sing bisa ngira.

Ana sawetara inti. Ayo ana Kontak. Ana huruf kanthi awak standar, saka ngendi parser lan hydrator nggawe kontak sing padha. A layang teka, kita maca, parsed menyang pasangan key-nilai, nggawe kontak, lan disimpen ing database.

Iku SD. Contone, yen kontak duwe properti Jeneng Lengkap, Umur lan Telpon Kontak. Data iki dikirim ing layang. Bisnis uga pengin dhukungan supaya bisa nambah kunci anyar kanthi cepet kanggo pemetaan properti entitas dadi pasangan ing awak surat. Yen ana wong sing salah ketik ing cithakan utawa sadurunge diluncurake, sampeyan kudu cepet-cepet miwiti pemetaan saka mitra anyar, adaptasi karo format anyar. Banjur kita bisa nambah korélasi pemetaan anyar minangka datafix murah. Yaiku conto urip.

Kita ngleksanakake, nggawe tes. Nyambut gawe.

Aku ora bakal menehi kode: ana akeh sumber, lan kasedhiya ing GitHub liwat link ing pungkasan artikel. Sampeyan bisa mbukak wong-wong mau, nyiksa wong-wong mau ngluwihi pangenalan lan ngukur wong-wong mau, minangka bakal mengaruhi ing kasus sampeyan. Aku mung bakal menehi kode saka rong cara cithakan sing mbedakake hydrator, kang mestine cepet, saka hydrator, kang mestine alon.

Logika minangka nderek: cara Cithakan nampa pasangan kui dening logika parser dhasar. Lapisan LINQ punika parser lan logika dhasar saka hydrator, kang nggawe panjalukan kanggo konteks database lan mbandhingaké tombol karo pasangan saka parser (kanggo fungsi iki ana kode tanpa LINQ kanggo comparison). Sabanjure, pasangan kasebut diterusake menyang metode hidrasi utama lan nilai pasangan disetel menyang properti sing cocog karo entitas kasebut.

"Cepet" (Awalan Cepet ing 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;
        }

Kaya sing kita deleng, koleksi statis kanthi properti setter digunakake - lambdas kompilasi sing nyebut entitas setter. Digawe dening kode ing ngisor iki:

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

Umumé cetha. Kita ngliwati properti kasebut, nggawe delegasi kanggo wong sing nelpon setter, lan simpen. Banjur kita nelpon yen perlu.

"Alon" (Awalan Alon ing pathokan):

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

Ing kene kita langsung ngliwati properti kasebut lan langsung nelpon SetValue.

Kanggo kajelasan lan minangka referensi, aku ngetrapake metode naif sing nulis nilai pasangan korelasi langsung menyang kolom entitas. Ater-ater - Manual.

Saiki ayo njupuk BenchmarkDotNet lan mriksa kinerja. Lan dumadakan ... (spoiler - iki dudu asil sing bener, rincian ing ngisor iki)

Artikel sing ora kasil babagan nyepetake refleksi

Apa sing kita deleng ing kene? Metode sing menang kanthi ater-ater Cepet dadi luwih alon ing meh kabeh pass tinimbang metode kanthi awalan Slow. Iki bener kanggo alokasi lan kacepetan kerja. Ing tangan liyane, implementasine ayu lan elegan saka pemetaan nggunakake cara LINQ dimaksudaké kanggo iki ngendi wae, ing nalisir, nemen nyuda produktivitas. Bedane yaiku urutan. Tren ora owah kanthi nomer pass sing beda. Bentenipun mung ing skala. Kanthi LINQ, 4 - 200 kaping luwih alon, luwih akeh sampah ing skala sing padha.

Dianyari

Aku ora percaya mripatku, nanging sing luwih penting, kancaku ora percaya karo mripatku utawa kodeku - Dmitry Tikhonov 0x1000000. Sawise mriksa maneh solusiku, dheweke nemokake kanthi apik lan nuduhake kesalahan sing aku ora kejawab amarga sawetara owah-owahan ing implementasine, wiwitan nganti pungkasan. Sawise ndandani bug sing ditemokake ing persiyapan Moq, kabeh asil tiba. Miturut asil retest, tren utama ora owah - LINQ isih mengaruhi kinerja luwih saka bayangan. Nanging, luwih becik yen karya karo kompilasi Ekspresi ora ditindakake kanthi sia-sia, lan asil kasebut katon ing alokasi lan wektu eksekusi. Bukak pisanan, nalika kolom statis diinisialisasi, kanthi alami luwih alon kanggo metode "cepet", nanging banjur owah-owahan kahanan.

Mangkene asil tes maneh:

Artikel sing ora kasil babagan nyepetake refleksi

Kesimpulan: nalika nggunakake refleksi ing perusahaan, ora ana trik khusus - LINQ bakal mangan produktivitas luwih akeh. Nanging, ing cara dhuwur-load sing mbutuhake optimasi, sampeyan bisa nyimpen bayangan ing wangun initializers lan utusan compiler, kang banjur bakal nyedhiyani logika "cepet". Kanthi cara iki sampeyan bisa njaga keluwesan refleksi lan kacepetan aplikasi.

Kode pathokan kasedhiya ing kene. Sapa wae bisa mriksa maneh tembungku:
HabraReflectionTests

PS: kode ing tes nggunakake IoC, lan ing pathokan nggunakake mbangun eksplisit. Kasunyatane yaiku ing implementasine pungkasan aku ngilangi kabeh faktor sing bisa mengaruhi kinerja lan nggawe rame.

PPS: Thanks kanggo pangguna Dmitry Tikhonov @0x1000000 kanggo nemokake kesalahanku ing nyetel Moq, kang mengaruhi pangukuran pisanan. Yen ana sing maca duwe karma sing cukup, mangga seneng. Wong lanang mandheg, wong kasebut maca, wong kasebut mriksa kaping pindho lan nuduhake kesalahane. Aku iki pantes diajeni lan simpati.

PPPS: matur nuwun kanggo maca tliti sing entuk dhasar gaya lan desain. Aku kanggo uniformity lan penak. Diplomasi presentasi ninggalake akeh sing dikarepake, nanging aku njupuk kritik kasebut. Mangga pindhah menyang projectile.

Source: www.habr.com

Add a comment