लगभग 3 सप्ताह पहले मैंने
डैपर के बारे में इस
विषय में हब पर पढ़ा -
ओआरएम लोकप्रिय साइट
स्टेपोमोफ़्लो के प्रमुख डेवलपर्स में से एक। इस सुपरहीरो का नाम
सैम केसर (इसके बाद बस सैम के रूप में
जाना जाता है) है। इसके अलावा, इससे पहले कि स्टैकओवरफ़्लो आर्किटेक्चर के बारे में विषय सामने आया, यह ज्ञात था कि इसमें
लिनक-टू-सकेल का उपयोग किया गया था। यह मुख्य कारण है कि मैं, अन्य डेवलपर्स की तरह, डैपर के स्रोत कोड का अध्ययन करना शुरू कर दिया। जैसा कि यह पता चला है कि यह एक छोटी, या सिर्फ एक फ़ाइल है। इसकी सावधानी से जांच करने के बाद, मैंने सोचा कि क्या यह और भी तेजी से किया जा सकता है। सैम के कोड को गति देना आसान नहीं था; यह बहुत अच्छा लिखा गया था। अगला, मैं अन्य डेवलपर्स के लिए युक्तियों के रूप में अपनी माइक्रोपोटीमाइजेशन का वर्णन करूंगा। लेकिन पहले, मैं कुछ डेवलपर्स को चेतावनी देना चाहता हूं। वर्णित अनुकूलन ने डैपर को 5% तक त्वरित किया और यह एक परियोजना के लिए आवश्यक है जैसे कि स्टैकओवरफ़्लो, लेकिन आपके प्रोजेक्ट के लिए महत्वपूर्ण नहीं हो सकता है। इसलिए,
हमेशा प्रोफाइलिंग परिणामों के आधार पर मैक्रोॉप्टीमाइजेशन (विषय के अंत में उदाहरण) के विकल्प पर विचार करें और केवल विशेष मामलों में ही माइक्रोएप्टिमाइजेशन का सहारा लें।
हमेशा एक न्यूनतम अनुबंध का उपयोग करें।
सख्ती से, यह केवल परिवर्तनों को बेहतर और अधिक प्रतिरोधी कोड बनाता है, लेकिन इसके निष्पादन को गति नहीं देता है। कभी-कभी सही अनुबंध निर्धारित करना आसान होता है, और कभी-कभी नहीं। उदाहरण के लिए, यदि यह IList को लौटाने के लिए कोई मतलब नहीं है, अगर बाकी कोड संग्रह पर एक साधारण पुनरावृत्ति करता है। बस IEnumerable लौटें। इस इंटरफ़ेस को चुनना सैम को अगले संस्करण में रिटर्न
यील्ड कंस्ट्रक्शन का उपयोग करने की अनुमति देता है:
public static IEnumerable<T> ExecuteMapperQuery<T>(this IDbConnection con, string sql, object param = null, SqlTransaction transaction = null) { using (var reader = GetReader(con, transaction, sql, GetParamInfo(param))) { var identity = new Identity(sql, con.ConnectionString, typeof(T)); var deserializer = GetDeserializer<T>(identity, reader); while (reader.Read()) { yield return deserializer(reader); } } }
एक स्पष्ट पसंद के रूप में, हम
IDataReader इंटरफ़ेस का उल्लेख करते हैं। सैम अक्सर इस इंटरफ़ेस का समर्थन करने वाली वस्तुओं के लिए
फील्डकाउंट संपत्ति का उपयोग करता है। यद्यपि, यदि आप इंटरफेस की पूरी पदानुक्रम की सावधानीपूर्वक जांच करते हैं, तो आप देख सकते हैं कि फील्डकाउंट वास्तव में
आईडीटाकोर इंटरफेस से
संबंधित है।
एक अनुबंध को हटाने पर विचार करें
यह सलाह पिछले एक का परिणाम है, इसलिए हम इस पर विस्तार से ध्यान नहीं देंगे। कभी-कभी एक अनुबंध इतना न्यूनतम होता है कि इसे सुरक्षित रूप से हटाया जा सकता है। नीचे दिए गए उदाहरण में,
IDbConnection के बजाय
, आप बस एक स्ट्रिंग पास कर सकते हैं:
private class Identity : IEquatable<Identity> { private readonly string connectionString; internal Identity(string sql, IDbConnection cnn, Type type) {
भविष्यवाणी करना सीखें
थोड़ा अजीब लगता है, है ना? हालाँकि, मैं यहाँ एक एल्गोरिथम के तार्किक व्यवहार का निर्धारण करने के बारे में बात कर रहा हूँ। भविष्यवाणियां सटीक और गलत हैं। पहले गलत पर विचार करें। यहाँ आप निश्चित रूप से नहीं कह सकते कि कैसे और क्या होगा। उदाहरण के लिए, डैपर ज्ञात प्रकारों के शब्दकोश को पॉप्युलेट करता है। हम जानते हैं कि जब एक निश्चित राशि पहुँच जाती है, तो उपयोगकर्ता को नए तत्वों को जोड़ने की स्थिति में शब्दकोश को अपना आकार बढ़ाने में समय लगेगा। इस मामले में हम क्या भविष्यवाणी कर सकते हैं? सरल बात यह है कि सभी प्रकारों को याद करें और शब्दकोश को बताएं कि हमें तुरंत कितनी स्मृति की आवश्यकता है। मुझे 35 मिले:
public static class SqlMapper { static readonly Dictionary<Type, DbType> typeMap; static SqlMapper() {
और अशुद्धि क्या है? यह संख्या परिवर्तनों पर अत्यधिक निर्भर है और एक नया प्रकार जोड़ते समय यह अमान्य होगा। बेशक, कुछ भी बुरा नहीं होगा, लेकिन कोड अधिक अविश्वसनीय हो जाता है, और भविष्यवाणी गलत है।
सटीक भविष्यवाणियां बहुत अच्छी हैं और आपको जहां भी संभव हो, उनका उपयोग करने की आवश्यकता है। इस तरह की भविष्यवाणी का एक हड़ताली उदाहरण एक सरणी के साथ एक सूची का प्रतिस्थापन है, जब तत्वों की संख्या बिल्कुल ज्ञात होती है। मुख्य कारण शब्दकोश के लिए समान है। अर्थात्, स्मृति का वास्तविक उत्थान। एक और महत्वपूर्ण कारण यह है कि किसी सूची में जोड़ें विधि को कॉल करने की तुलना में किसी सरणी में अनुक्रमणिका द्वारा असाइनमेंट ऑपरेशन बहुत तेज़ है। यह कोड पीढ़ी के उदाहरण में स्पष्ट रूप से देखा गया है:
private static Func<object, List<ParamInfo>> CreateParamInfoGenerator(Type type) { DynamicMethod dm = new DynamicMethod("ParamInfo" + Guid.NewGuid().ToString(), typeof(List<ParamInfo>), new Type[] { typeof(object) }, true); var il = dm.GetILGenerator();
और सरणी के लिए:
private static Func<object, IEnumerable<ParamInfo>> CreateParamInfoGenerator(Type type) { var dm = new DynamicMethod("ParamInfo" + Guid.NewGuid(), typeof(IEnumerable<ParamInfo>), new[] { typeof(object) }, true); var il = dm.GetILGenerator();
प्रतिबिंब का उपयोग कम करें
सलाह स्पष्ट है और सभी जानते हैं कि प्रतिबिंब एक अत्यंत धीमा तंत्र है। सैम यह भी जानता है और वह वस्तुओं के तरीकों और गुणों के साथ काम में तेजी लाने के लिए कोड पीढ़ी का उपयोग करता है (इस मामले में, मैं मैनुअल पीढ़ी के खिलाफ हूं और अभिव्यक्ति के पेड़ को एक योग्य प्रतिस्थापन मानता हूं)। प्रतिबिंब की लागत से निपटने के लिए एक दूसरा आम तौर पर स्वीकृत तरीका है - कैशिंग। डापर में, प्रत्येक नए वर्ग के लिए एक deserializer बनाया जाता है। निर्माण कोड में, जिसे आप लाइन पा सकते हैं:
var getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int)) .Select(p => p.GetGetMethod()).First();
जाहिर है, इस जानकारी को कैश किया जा सकता है। GetItem को क्लास लेवल पर ले जाएं और स्टेटिक कंस्ट्रक्टर में इनिशियलाइज़ करें।
डबल चेक सर्किट
सबसे अधिक बार, प्रोग्रामर क्लोजर बनाते हैं (उन लोगों के लिए जो सी # में उनसे पहले नहीं मिले हैं, मैं इस
लिंक का पालन करने की सलाह देता हूं) होशपूर्वक नहीं, और वे उन्हें अप्रत्याशित त्रुटियों के साथ भुगतान करते हैं (सैम ने भी पकड़ा!)। हालांकि, क्लोजर का उपयोग तेजी लाने के लिए किया जा सकता है। एक उदाहरण:
private static object GetDynamicDeserializer(IDataReader reader) { List<string> colNames = new List<string>(); for (int i = 0; i < reader.FieldCount; i++) { colNames.Add(reader.GetName(i)); } Func<IDataReader, ExpandoObject> rval = r => { IDictionary<string, object> row = new ExpandoObject(); int i = 0; foreach (var colName in colNames) { var tmp = r.GetValue(i); row[colName] = tmp == DBNull.Value ? null : tmp; i++; } return (ExpandoObject)row; }; return rval; }
जैसा कि आप लैम्ब्डा अभिव्यक्ति में देख सकते हैं, स्तंभ नामों की प्राप्ति को गति देने के लिए स्थानीय चर कोलनेम पर एक क्लोजर बनाया गया है। सैद्धांतिक रूप से, यह एक प्रदर्शन को बढ़ावा दे सकता है। आखिरकार, जब हम IDataReader में सभी रिकॉर्डों पर पुनरावृति करते हैं, तो स्तंभों का नाम नहीं बदलता है। दुर्भाग्य से, उदाहरण के लिए,
SqlDataReader डेवलपर्स ने भी इस बारे में सोचा और स्तंभ नामों को वर्ग के अंदर एक समान सरणी में संग्रहीत किया, इसलिए निम्न कोड पिछले एक के समान होगा, लेकिन बिना बंद किए:
private static Func<IDataRecord, ExpandoObject> GetDynamicDeserializer() { return r => { IDictionary<string, object> row = new ExpandoObject(); for (var i = 0; i < r.FieldCount; i++) { var tmp = r.GetValue(i); row[r.GetName(i)] = tmp == DBNull.Value ? null : tmp; } return (ExpandoObject)row; }; }
स्ट्रिंग्स के लिए कई कॉन्टैक्शन ऑपरेशन से बचें
हां, मुझे पता है कि प्रत्येक .Net डेवलपर जानता है कि आपको कई लाइनों का एक स्ट्रिंग बनाने के लिए स्ट्रिंगबर्ल का उपयोग करने की आवश्यकता है। लेकिन यह कितनी लाइनें हैं? दो या तीन पंक्तियों के लिए, स्ट्रिंगब्यूलर बनाना व्यर्थ हो सकता है। एक उदाहरण:
private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction tranaction, string sql, List<ParamInfo> paramInfo) {
हम स्ट्रिंग में रुचि रखते हैं, जो "@" + info.Name + i के रूप में बनती है। यह
IDbCommand पैरामीटर का नाम है। और स्मृति में ऐसे प्रत्येक नाम के लिए तीन लाइनें बनाई जाती हैं। यदि हमारे पैरामीटर को टेक्स्ट कहा जाता है, तो लाइनें इस तरह दिखेंगी:
@ @text @text1
सिद्धांत रूप में, थोड़ा, लेकिन 5 मापदंडों के लिए हमारे पास 15 लाइनें होंगी। स्ट्रिंगबर्ल के लिए समय? नहीं, शायद नहीं। बाकी कोड का विश्लेषण करने के बाद, आप देख सकते हैं कि निर्माण "@" + info.Name बहुत बार उपयोग किया जाता है, इसलिए इसे चर जानकारी के साथ बदलें। तो हम लाइनों पर और इसके अलावा संपत्ति तक पहुँचने पर बचाते हैं। नतीजतन, 5 मापदंडों के लिए, केवल 6 लाइनें (सूचना पर एक नाम और प्रत्येक संघनन ऑपरेशन के लिए एक)।
मैं जारी रख सकता हूं और इस तरह की तुच्छ चीजों के बारे में बात कर सकता हूं जैसे कि चर का उपयोग उनके स्थान के करीब संभव के रूप में या इसके विपरीत उन्हें लूप से बाहर ले जाना, अगर-बयान में अनावश्यक शाखाओं को त्यागना, उनके उपयोग के स्थान पर छोटी विधियों को एम्बेड करना। लेकिन मैं स्थूल अनुकूलन के बारे में बेहतर बात कर रहा हूँ। सैम वर्तमान में IDbCommand में मापदंडों को जोड़ने में तेजी लाने के लिए काम कर रहा है। एक बाहरी पर्यवेक्षक के रूप में, मैं आपको आदेशों के पुन: उपयोग और उनकी तैयारी पर ध्यान देने की सलाह दे सकता हूं (यह
SqlCommand और
तैयार विधि के लिए ठीक काम करता है)।
हो सकता है कि जब डॅपर काम करने से लेकर रिलीज़ होने तक जाता है, तो मैं एक और समीक्षा करूँगा, लेकिन अभी के लिए मैं इस परियोजना की सावधानीपूर्वक निगरानी करूँगा और इसके लिए शुभकामनाएँ देता हूँ।
पुनश्च: लेखक ने अपने नागरिक कर्तव्य को पूरा करने की कोशिश की और गिटहब को एक पुल अनुरोध भेजा, लेकिन, दुर्भाग्य से, जबकि लेखक हैबर पर एक विषय लिख रहा था, सैम ने डैपर को विकसित किया और अनुरोध अप्रासंगिक हो गया। हालांकि, लेखक ने सैम को लिखा और उन्होंने डैपर की रिहाई में सभी इच्छाओं को ध्यान में रखने का वादा किया।