مون کي مضمون جي عنوان کي فوري طور تي بيان ڪرڻ ڏيو. شروعات ۾، مون هڪ سادي پر حقيقي مثال استعمال ڪندي عڪاسي کي تيز ڪرڻ بابت سٺي، قابل اعتماد صلاح ڏيڻ جو ارادو ڪيو. بهرحال، بينچ مارڪنگ دوران، اهو ظاهر ٿيو ته عڪاسي ايتري سست نه هئي جيترو مون سوچيو هو، ۽ LINQ مون ڪڏهن به تصور کان سست هو. ۽ اهو ظاهر ٿيو ته مون هڪ ماپ جي غلطي پڻ ڪئي هئي... هن حقيقي زندگي جي ڪهاڻي جي تفصيل ڪٽ کان هيٺ ۽ تبصرن ۾ آهن. جيئن ته مثال ڪافي عام آهي ۽ انهي طريقي سان لاڳو ڪيو ويو آهي جيڪو عام طور تي انٽرپرائز ۾ ڪيو ويندو آهي، اهو منهنجي خيال ۾ هڪ دلچسپ مظاهرو ثابت ٿيو: مضمون جي مکيه موضوع جي ڪارڪردگي تي اثر ٻاهرين منطق جي ڪري قابل ذڪر نه هو: Moq، Autofac، EF ڪور، ۽ ٻيا "بائنڊنگز."
مون هن مضمون جي تاثر هيٺ ڪم ڪرڻ شروع ڪيو:
جيئن توهان ڏسي سگهو ٿا، ليکڪ تجويز ڪري ٿو ته مرتب ڪيل ڊيليگيٽس کي استعمال ڪرڻ بدران سڌو سنئون ريفلڪشن قسم جي طريقن تائين رسائي حاصل ڪرڻ جي بدران هڪ بهترين طريقي سان ايپليڪيشن جي ڪارڪردگي کي تيز ڪرڻ لاءِ. يقيناً، IL اخراج پڻ آهي، پر ان کان بچڻ بهتر آهي، ڇاڪاڻ ته اهو ڪم کي پورو ڪرڻ جو سڀ کان وڌيڪ محنت طلب ۽ غلطي جو شڪار طريقو آهي.
اهو غور ڪندي ته مون هميشه غور جي رفتار بابت ساڳي راءِ رکي آهي، مون ليکڪ جي نتيجن تي خاص طور تي سوال ڪرڻ جو ارادو نه ڪيو.
مون کي اڪثر انٽرپرائز ۾ عڪاسي جي سادي استعمالن جو منهن ڏسڻو پوي ٿو. هڪ قسم ورتو ويندو آهي. ملڪيت جي معلومات حاصل ڪئي ويندي آهي. سيٽ ويليو طريقو سڏيو ويندو آهي، ۽ هرڪو خوش هوندو آهي. قدر ٽارگيٽ فيلڊ ۾ اچي وئي آهي، هرڪو خوش هوندو آهي. هوشيار ماڻهو - سينئر ۽ ٽيم ليڊز - اعتراض ڪرڻ لاءِ پنهنجون ايڪسٽينشن لکندا آهن، انهن جي "يونيورسل" ميپرز کي هڪ قسم کان ٻئي تائين هن سادي عمل تي ٻڌل آهي. خلاصو عام طور تي هي آهي: سڀئي فيلڊ وٺو، سڀئي ملڪيتون وٺو، انهن تي ٻيهر ورجايو: جيڪڏهن قسم جي ميمبر جا نالا ملن ٿا، ته اسان سيٽ ويليو کي عمل ۾ آڻيون ٿا. اسان وقتاً فوقتاً استثنا کي پڪڙيندا آهيون جڏهن ڪنهن هڪ قسم لاءِ ملڪيت نه ملي، پر هتي به ڪارڪردگي کي بهتر بڻائڻ جو هڪ طريقو آهي: ڪوشش ڪريو/پڪڙو.
مون ماڻهن کي پارسر ۽ ميپر ٻيهر ايجاد ڪندي ڏٺو آهي بغير مڪمل طور تي ڄاڻ ڏني وئي ته انهن کان اڳ ايجاد ٿيل ڦيٿا ڪيئن ڪم ڪندا هئا. مون ماڻهن کي پنهنجي بيوقوفيءَ واري عمل کي حڪمت عملين، انٽرفيس ۽ انجيڪشن جي پويان لڪائيندي ڏٺو آهي، جيئن ته اهو بعد ۾ ٿيندڙ تباهي کي معاف ڪندو. مون اهڙين عملن تي پنهنجو منهن ڦيرايو. حقيقت ۾، مون اصل ڪارڪردگي جي گهٽتائي کي ماپ نه ڪيو، ۽ جڏهن به ممڪن ٿيو، مون صرف وقت ملڻ تي وڌيڪ "بهترين" عمل لاءِ عمل کي تبديل ڪيو. تنهن ڪري هيٺ ڏنل بحث ڪيل شروعاتي ماپن، مون کي سنجيدگي سان پريشان ڪيو.
مونکي لڳي ٿو ته توهان مان ڪيترائي، رچرٽر يا ٻين نظرياتي ماهرن کي پڙهڻ دوران، مڪمل طور تي منصفانه دعويٰ سان مليا آهن ته ڪوڊ ۾ عڪاسي هڪ اهڙو رجحان آهي جيڪو ايپليڪيشن جي ڪارڪردگي تي انتهائي منفي اثر وجهي ٿو.
ڪالنگ ريفلڪشن CLR کي اسيمبلين کي پار ڪرڻ تي مجبور ڪري ٿو ته جيئن صحيح هڪ ڳولي سگهجي، انهن جو ميٽا ڊيٽا حاصل ڪري سگهجي، ان کي پارس ڪري سگهجي، وغيره. وڌيڪ، تسلسل ٽرورسل دوران ريفلڪشن وڏي ميموري مختص ڪرڻ جي طرف وٺي ٿو. اسان ميموري استعمال ڪريون ٿا، CLR GC کي پيڪ ڪري ٿو، ۽ ensu کي منجمد ڪري ٿو. يقين ڪريو، اهو خاص طور تي سست هجڻ گهرجي. جديد پيداوار سرورز يا ڪلائوڊ مشينن ۾ ميموري جي وڏي مقدار اعلي پروسيسنگ دير کي روڪي نه ٿي. حقيقت ۾، توهان وٽ جيتري وڌيڪ ميموري هوندي، اوترو ئي وڌيڪ امڪان اهو هوندو ته توهان 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;
}
جيئن اسان ڏسي سگهون ٿا، پراپرٽي سيٽرز سان گڏ هڪ جامد ڪليڪشن استعمال ڪيو ويندو آهي - مرتب ڪيل ليمبڊا جيڪي اينٽيٽي جي سيٽر کي سڏيندا آهن. اهي هيٺ ڏنل ڪوڊ سان ٺاهيا ويا آهن:
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 ڀيرا سست آهي، تقريبن ساڳئي مقدار ۾ ڪچري سان.
اپڊيٽ
مون کي پنهنجين اکين تي يقين نه آيو، پر وڌيڪ اهم ڳالهه اها ته، اسان جي ساٿي کي نه منهنجين اکين تي ۽ نه ئي منهنجي ڪوڊ تي يقين آيو - منهنجي حل جي ٻيهر جانچ ڪرڻ کان پوءِ، هن شاندار طريقي سان هڪ غلطي ڏٺي ۽ نشاندهي ڪئي جيڪا مون شروعاتي عمل درآمد ۾ ڪيترين ئي تبديلين جي ڪري وڃائي ڇڏي هئي. Moq سيٽ اپ ۾ دريافت ٿيل بگ کي درست ڪرڻ کان پوءِ، سڀئي نتيجا معمول تي واپس آيا. ٻيهر ٽيسٽ جا نتيجا ڏيکارين ٿا ته مکيه رجحان تبديل نه ٿيو آهي - LINQ اڃا تائين عڪاسي کان وڌيڪ ڪارڪردگي تي اثر انداز ٿئي ٿو. بهرحال، اهو ڏسي سٺو لڳندو آهي ته ايڪسپريشنز کي مرتب ڪرڻ جو ڪم قابل قدر آهي، ۽ نتيجا مختص ۽ عملدرآمد جي وقت ٻنهي ۾ نظر اچن ٿا. پهرين رن، جڏهن جامد شعبا شروع ڪيا ويندا آهن، قدرتي طور تي "تيز" طريقي لاءِ سست هوندو آهي، پر پوءِ صورتحال تبديل ٿي ويندي آهي.
هتي ٻيهر ٽيسٽ جو نتيجو آهي:

نتيجو: جڏهن انٽرپرائز ۾ عڪاسي استعمال ڪندي، چالاڪين جو سهارو وٺڻ جي ڪا ضرورت ناهي - LINQ ڪارڪردگي کي خاص طور تي کائي ويندو. جڏهن ته، هاءِ لوڊ طريقن ۾ جن کي اصلاح جي ضرورت هوندي آهي، عڪاسي کي شروعاتي ۽ ڊيليگيٽ ڪمپائلرز جي صورت ۾ محفوظ ڪري سگهجي ٿو، جيڪي پوءِ "تيز" منطق فراهم ڪندا. هن طريقي سان، توهان عڪاسي جي لچڪ ۽ توهان جي ايپليڪيشن جي رفتار ٻنهي کي محفوظ ڪري سگهو ٿا.
بينچ مارڪ ڪوڊ هتي موجود آهي. ڪو به دلچسپي رکندڙ منهنجي بيانن کي ٻيهر چيڪ ڪري سگهي ٿو:
پي ايس: ٽيسٽن ۾ ڪوڊ IoC استعمال ڪري ٿو، جڏهن ته معيار هڪ واضح تعمير استعمال ڪن ٿا. اهو ئي سبب آهي جو آخري عملدرآمد ۾، مون سڀني عنصرن کي ختم ڪري ڇڏيو جيڪي ڪارڪردگي تي اثر انداز ٿي سگهن ٿا ۽ نتيجن کي خراب ڪري سگهن ٿا.
پي پي ايس: استعمال ڪندڙ جي مهرباني Moq سيٽ اپ ۾ منهنجي غلطي کي ڏسڻ لاءِ، جنهن شروعاتي ماپن کي متاثر ڪيو. جيڪڏهن ڪنهن پڙهندڙ وٽ ڪافي ڪرم آهي، ته مهرباني ڪري ان کي پسند ڪريو. ڪنهن روڪيو، ڪنهن غور سان پڙهيو، ڪنهن ٻه ڀيرا چيڪ ڪيو، ۽ غلطي جي نشاندهي ڪئي. مون کي لڳي ٿو ته هي احترام ۽ همدردي جي لائق آهي.
پي پي پي ايس: محتاط پڙهندڙ جو شڪريو جنهن انداز ۽ ترتيب ۾ کوٽائي ڪئي. مان مڪمل طور تي تسلسل ۽ سهولت لاءِ آهيان. پيشڪش جي سفارتڪاري ۾ گهڻو ڪجهه گهربل آهي، پر مون تنقيد کي نظر ۾ رکيو آهي. مهرباني ڪري ڪم شروع ڪريو.
جو ذريعو: www.habr.com
