Тусгал хурдасгах тухай амжилтгүй нийтлэл

Би нийтлэлийн гарчгийг нэн даруй тайлбарлах болно. Анхны төлөвлөгөө нь энгийн хэрнээ бодит жишээн дээр тусгалыг хэрхэн хурдасгах талаар сайн, найдвартай зөвлөгөө өгөх байсан боловч жишиг судалгааны явцад тусгал нь миний бодсон шиг удаан биш, LINQ миний хар дарсан зүүднээс удаан байдаг нь тогтоогдсон. Гэвч эцэст нь би бас хэмжилтэнд алдаа гаргасан нь тодорхой болсон... Энэхүү амьдралын түүхийн дэлгэрэнгүйг захын доор болон сэтгэгдэлд оруулсан байгаа. Энэ жишээ нь нэлээд түгээмэл бөгөөд зарчмын хувьд ихэвчлэн аж ахуйн нэгжид хийгддэг тул энэ нь маш сонирхолтой, миний бодлоор амьдралын жишээ болж хувирав: нийтлэлийн гол сэдвийн хурдад нөлөөлсөн. гадаад логикийн улмаас мэдэгдэхүйц биш: Moq, Autofac, EF Core болон бусад "bandings".

Би энэ нийтлэлийн сэтгэгдэл дор ажиллаж эхэлсэн. Тусгал яагаад удаан байдаг вэ?

Таны харж байгаагаар зохиогч тусгалын төрлийн аргуудыг шууд дуудахын оронд эмхэтгэсэн төлөөлөгчдийг програмыг хурдасгах гайхалтай арга болгон ашиглахыг санал болгож байна. Мэдээжийн хэрэг IL ялгаруулалт байдаг, гэхдээ энэ нь алдаатай даалгаврыг гүйцэтгэх хамгийн их хөдөлмөр шаардсан арга учраас би үүнээс зайлсхийхийг хүсч байна.

Би эргэцүүлэн бодох хурдны талаар ижил төстэй үзэл бодолтой байсаар ирсэн тул зохиогчийн дүгнэлтэд эргэлзэх бодолгүй байсан.

Би аж ахуйн нэгжид тусгалын гэнэн хэрэглээтэй байнга тулгардаг. Төрлийг нь авсан. Үл хөдлөх хөрөнгийн талаарх мэдээллийг авдаг. SetValue аргыг дуудаж, хүн бүр баярладаг. Зорилтот талбарт үнэ цэнэ ирлээ, бүгд баяртай байна. Маш ухаалаг хүмүүс - ахмад настнууд болон багийн ахлагч нар нэг төрлийн "бүх нийтийн" зураглагчид ийм гэнэн хэрэгжүүлэлт дээр үндэслэн объектын өргөтгөлүүдийг бичдэг. Үүний мөн чанар нь ихэвчлэн ийм байдаг: бид бүх талбаруудыг авч, бүх шинж чанарыг нь авч, тэдгээрийг давтдаг: хэрэв төрлийн гишүүдийн нэр таарч байвал бид SetValue-ийг ажиллуулна. Үе үе бид аль нэг төрлийн өмчийг олж чадаагүй алдааны улмаас үл хамаарах зүйлийг олж авдаг, гэхдээ энд ч гэсэн гүйцэтгэлийг сайжруулах арга зам байдаг. Оролдоод үзээрэй/барих.

Хүмүүс өөрсдийнхөө өмнө гарч ирсэн машинууд хэрхэн ажилладаг тухай мэдээллээр бүрэн хангагдаагүй байж задлан шинжлэгч, зураглагчийг дахин зохион бүтээж байгааг би харсан. Хүмүүс стратегийн ард, интерфэйсүүдийн ард, тарилгын ард гэнэн хэрэгжүүлэлтээ нууж байхыг би харсан бөгөөд энэ нь дараагийн бачналиа уучлах мэт. Ийм зүйлийг ухаарсандаа би хамраа дээшлүүлсэн. Үнэн хэрэгтээ, би гүйцэтгэлийн бодит алдагдлыг хэмжээгүй бөгөөд хэрэв боломжтой бол би үүнийг гартаа авч чадвал хэрэгжилтийг илүү "оновчтой" болгон өөрчилсөн. Тиймээс доор авч үзсэн анхны хэмжилтүүд намайг маш их төөрөлдүүлсэн.

Рихтер болон бусад үзэл сурталчдыг уншиж байхдаа та нарын олонхи нь кодын тусгал нь програмын гүйцэтгэлд маш сөрөг нөлөө үзүүлдэг үзэгдэл юм гэсэн бүрэн шударга мэдэгдлийг олж мэдсэн гэж би бодож байна.

Дуудлагын тусгал нь CLR-г шаардлагатай нэгийг нь олохын тулд ассемблеруудаар дамжиж, мета өгөгдлийг нь татаж авах, задлан шинжлэх гэх мэтийг шаарддаг. Нэмж дурдахад, дарааллаар дамжих явцад эргэцүүлэн бодох нь их хэмжээний санах ойг хуваарилахад хүргэдэг. Бид санах ойг ашиглаж байна, CLR нь GC-г илрүүлж, фризүүд эхэлдэг. Энэ нь мэдэгдэхүйц удаан байх ёстой, надад итгээрэй. Орчин үеийн үйлдвэрлэлийн серверүүд эсвэл үүл машинууд дээрх асар их хэмжээний санах ой нь боловсруулалтын өндөр саатлаас сэргийлж чадахгүй. Үнэн хэрэгтээ санах ой их байх тусам та GC хэрхэн ажилладагийг АНХААРАХ магадлал өндөр байдаг. Тусгал нь онолын хувьд түүний хувьд нэмэлт улаан өөдөс юм.

Гэсэн хэдий ч бид бүгдээрээ IoC контейнер, огнооны зураглагчийг ашигладаг бөгөөд тэдгээрийн үйл ажиллагааны зарчим нь тусгал дээр суурилдаг боловч тэдгээрийн гүйцэтгэлийн талаар ихэвчлэн асуулт байдаггүй. Үгүй ээ, гадны хязгаарлагдмал контекст загвараас хамаарал, хийсвэрлэлийг нэвтрүүлэх нь ямар ч тохиолдолд гүйцэтгэлийг золиослоход зайлшгүй шаардлагатай учраас биш юм. Бүх зүйл илүү энгийн - энэ нь гүйцэтгэлд тийм ч их нөлөөлдөггүй.

Баримт нь тусгалын технологид суурилсан хамгийн түгээмэл хүрээ нь түүнтэй илүү оновчтой ажиллахын тулд бүх төрлийн заль мэхийг ашигладаг. Ихэвчлэн энэ нь кэш юм. Ихэвчлэн эдгээр нь илэрхийллийн модноос эмхэтгэсэн илэрхийлэл ба төлөөлөгчид юм. Ижил автомат толь бичиг нь тусгалыг дуудахгүйгээр нэгийг нөгөө рүү хөрвүүлэх функц бүхий төрлүүдэд тохирсон толь бичгийг хадгалдаг.

Үүнд хэрхэн хүрч байна вэ? Үндсэндээ энэ нь платформ өөрөө JIT код үүсгэхэд ашигладаг логикоос ялгаатай биш юм. Аргыг анх удаа дуудах үед түүнийг эмхэтгэдэг (тиймээ, энэ процесс хурдан биш); дараагийн дуудлагад хяналтыг аль хэдийн хөрвүүлсэн арга руу шилжүүлдэг бөгөөд гүйцэтгэлийн мэдэгдэхүйц бууралт байхгүй болно.

Манай тохиолдолд та JIT эмхэтгэлийг ашиглаж, дараа нь эмхэтгэсэн зан төлөвийг AOT-ийн ижил төстэй гүйцэтгэлтэй ашиглаж болно. Энэ тохиолдолд илэрхийлэл бидэнд туслах болно.

Энэ зарчмыг товчхондоо дараах байдлаар илэрхийлж болно.
Та эргэцүүлэн бодох эцсийн үр дүнг хөрвүүлсэн функц агуулсан төлөөлөгчөөр кэшлэх хэрэгтэй. Мөн объектын гадна хадгалагдаж буй өөрийн төрлийн ажилчин талбарт шаардлагатай бүх объектыг төрлийн мэдээлэл бүхий кэш хийх нь утга учиртай.

Үүнд логик бий. Хэрэв ямар нэг зүйлийг эмхэтгэж, кэш хийх боломжтой бол үүнийг хийх хэрэгтэй гэдгийг эрүүл ухаан бидэнд хэлдэг.

Урагшаа харахад, илэрхийлэл эмхэтгэх санал болгож буй аргыг ашиглаагүй байсан ч тусгалтай ажиллах кэш нь давуу талтай гэдгийг хэлэх хэрэгтэй. Үнэндээ би дээр дурдсан нийтлэлийн зохиогчийн тезисийг давтаж байна.

Одоо кодын тухай. Ноцтой зээлийн байгууллагын ноцтой үйлдвэрлэлд тулгарч байсан миний сүүлийн үеийн өвдөлт дээр үндэслэсэн жишээг харцгаая. Бүх аж ахуйн нэгжүүд зохиомол байдаг тул хэн ч тааварлахгүй.

Зарим мөн чанар байдаг. Холбоо барих байх. Стандартчилсан биетэй үсэг байдаг бөгөөд тэдгээрээс задлан шинжлэгч ба усжуулагч нь ижил контактуудыг үүсгэдэг. Захиа ирсэн, бид үүнийг уншиж, түлхүүр-утга хос болгон задлан шинжилж, харилцагч үүсгээд мэдээллийн санд хадгалсан.

Энэ нь анхан шатны. Харилцагч нь овог нэр, нас, холбоо барих утас гэсэн шинж чанартай гэж бодъё. Энэ өгөгдлийг захидалд дамжуулдаг. Бизнес нь мөн захидлын үндсэн хэсэгт аж ахуйн нэгжийн шинж чанарыг хос болгон зураглах шинэ түлхүүрүүдийг хурдан нэмж оруулахад дэмжлэг үзүүлэхийг хүсч байна. Хэрэв хэн нэгэн загварт үсгийн алдаа гаргасан эсвэл гарахаас өмнө шинэ түншээс шинэ форматад дасан зохицох зураглалыг яаралтай эхлүүлэх шаардлагатай бол. Дараа нь бид хямд өгөгдөл засах шинэ зураглалын хамаарлыг нэмж болно. Энэ бол амьдралын жишээ юм.

Бид хэрэгжүүлдэг, туршилт хийдэг. Ажилладаг.

Би код өгөхгүй: маш олон эх сурвалж байгаа бөгөөд тэдгээрийг GitHub дээр нийтлэлийн төгсгөлд байгаа холбоосоор авах боломжтой. Та тэдгээрийг ачаалж, танигдахааргүй тамлан зовоож, хэмжиж болно, учир нь энэ нь таны хэрэгт нөлөөлнө. Би зөвхөн хурдан байх ёстой байсан гидраторыг удаан байх ёстой гидратороос ялгах хоёр загвар аргын кодыг л өгье.

Логик нь дараах байдалтай байна: загвар арга нь үндсэн задлан шинжлэх логикоор үүсгэгдсэн хосуудыг хүлээн авдаг. LINQ давхарга нь өгөгдлийн сангийн контекст хүсэлт гаргаж, задлан шинжлэгчийн хосуудтай түлхүүрүүдийг харьцуулдаг гидраторын задлан шинжлэгч ба үндсэн логик юм (эдгээр функцүүдийн хувьд LINQ-гүй харьцуулах код байдаг). Дараа нь хосуудыг усжуулах үндсэн аргад шилжүүлж, хосуудын утгыг тухайн объектын харгалзах шинж чанарт тохируулна.

"Хурдан" (Жишиг үзүүлэлт дэх Хурдан угтвар):

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

Бидний харж байгаагаар тохируулагч шинж чанартай статик цуглуулгыг ашигладаг - тохируулагч нэгжийг дууддаг эмхэтгэсэн lambdas. Дараах кодоор үүсгэгдсэн:

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

Ерөнхийдөө ойлгомжтой. Бид шинж чанаруудыг тойрч, тохируулагчийг дууддаг төлөөлөгчдийг үүсгэж, хадгалдаг. Дараа нь бид шаардлагатай үед дууддаг.

“Удаан” (Жишиг үзүүлэлт дэх удаашралтай угтвар):

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

Энд бид шинж чанаруудыг нэн даруй тойрч SetValue-г шууд дууддаг.

Тодорхой болгох үүднээс би тэдгээрийн хамаарлын хосуудын утгыг объектын талбарт шууд бичдэг гэнэн аргыг хэрэгжүүлсэн. Угтвар - Гарын авлага.

Одоо BenchmarkDotNet-ийг авч гүйцэтгэлийг шалгацгаая. Тэгээд гэнэт... (спойлер - энэ нь зөв үр дүн биш, дэлгэрэнгүй мэдээллийг доор харуулав)

Тусгал хурдасгах тухай амжилтгүй нийтлэл

Бид энд юу харж байна вэ? Хурдан угтварыг ялан дийлэх аргууд нь Удаан угтвартай аргуудаас бараг бүх дамжуулалтад удаан байдаг. Энэ нь ажлын хуваарилалт, хурдны аль алинд нь үнэн юм. Нөгөөтэйгүүр, LINQ аргыг ашиглан зураглал хийх сайхан, гоёмсог хэрэгжилт нь боломжтой бол бүтээмжийг эрс бууруулдаг. Ялгаа нь дараалал юм. Янз бүрийн дамжуулалтын тоогоор чиг хандлага өөрчлөгддөггүй. Ганц ялгаа нь цар хүрээ юм. LINQ-тэй бол энэ нь 4-200 дахин удаан, ойролцоогоор ижил хэмжээний хог хаягдал ихтэй байдаг.

шинэчлэгдсэн

Би нүдэндээ итгээгүй, гэхдээ хамгийн чухал нь манай хамтрагч миний нүдэнд ч, миний кодонд ч итгээгүй - Дмитрий Тихонов 0x1000000. Миний шийдлийг дахин нягталж үзээд тэрээр анхан шатнаас эцсийнх хүртэл хэрэгжилтийн хэд хэдэн өөрчлөлтийн улмаас миний орхигдсон алдааг гайхалтай олж, зааж өгсөн. Moq тохиргоонд олдсон алдааг зассаны дараа бүх үр дүн байрандаа орсон. Дахин туршилтын үр дүнгээс харахад үндсэн чиг хандлага өөрчлөгдөөгүй - LINQ тусгалаас илүү гүйцэтгэлд нөлөөлдөг хэвээр байна. Гэсэн хэдий ч илэрхийлэл эмхэтгэх ажил дэмий хоосон хийгдээгүй бөгөөд үр дүн нь хуваарилалт, гүйцэтгэлийн хугацаанд хоёуланд нь харагдаж байгаа нь сайхан хэрэг юм. Статик талбаруудыг эхлүүлэх үед анхны хөөргөлт нь "хурдан" аргын хувьд аяндаа удаашрах боловч дараа нь нөхцөл байдал өөрчлөгдөнө.

Дахин шалгалтын үр дүн энд байна:

Тусгал хурдасгах тухай амжилтгүй нийтлэл

Дүгнэлт: аж ахуйн нэгжид тусгалыг ашиглахдаа заль мэх хийх шаардлагагүй - LINQ нь бүтээмжийг илүү ихээр идэх болно. Гэсэн хэдий ч оновчлол шаарддаг өндөр ачаалалтай аргуудын хувьд та тусгалыг эхлүүлэгч болон төлөөлөгч хөрвүүлэгч хэлбэрээр хадгалах боломжтой бөгөөд энэ нь "хурдан" логикийг өгөх болно. Ингэснээр та тусгалын уян хатан байдал болон хэрэглээний хурдыг хоёуланг нь хадгалах боломжтой.

Жишиг кодыг эндээс авах боломжтой. Хэн ч миний үгийг дахин шалгаж болно:
HabraReflection Tests

Жич: Туршилтын код нь IoC-г ашигладаг бөгөөд жишиг үзүүлэлтүүдэд тодорхой бүтцийг ашигладаг. Эцсийн хэрэгжилтэнд би гүйцэтгэлд нөлөөлж, үр дүнг шуугиантай болгож болох бүх хүчин зүйлийг таслан зогсоосон явдал юм.

PPS: Хэрэглэгчид баярлалаа Дмитрий Тихонов @0x1000000 Эхний хэмжилтэд нөлөөлсөн Moq-г тохируулахдаа миний алдааг олж мэдсэнийхээ төлөө. Уншигчдаас хангалттай үйлийн үртэй хүн байвал лайк дарна уу. Тэр хүн зогсоод, хүн уншаад, хүн давхар шалгаад алдааг зааж өгсөн. Энэ нь хүндэтгэл, өрөвдөх сэтгэлтэй байх ёстой гэж би бодож байна.

PPPS: Загвар, дизайны ёроолд хүрсэн нягт нямбай уншигчдад баярлалаа. Би нэгдмэл байдал, тав тухтай байдлын төлөө байна. Танилцуулгын дипломат байдал нь маш их хүсэх зүйл үлдээдэг ч би шүүмжлэлийг харгалзан үзсэн. Би сум асууж байна.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх