TLDR:
प्रदर्शन और खराब अभ्यास के दावों के साथ बहुत सारे उत्तर, इसलिए मैं यहां स्पष्ट करता हूं।
अपवादित मार्ग अधिक संख्या में लौटे स्तंभों के लिए तेज़ है, लूप मार्ग स्तंभों की कम संख्या के लिए तेज़ है, और क्रॉसओवर बिंदु लगभग 11 कॉलम है। ग्राफ़ और परीक्षण कोड देखने के लिए नीचे स्क्रॉल करें।
पूर्ण उत्तर:
शीर्ष के कुछ उत्तर के लिए कोड काम करता है, लेकिन तर्क में अपवाद से निपटने की स्वीकृति के आधार पर "बेहतर" उत्तर के लिए यहां एक अंतर्निहित बहस है और यह संबंधित प्रदर्शन है।
यह स्पष्ट करने के लिए कि मुझे विश्वास नहीं है कि कैटचिंग अपवादों के बारे में बहुत मार्गदर्शन है। Microsoft के पास थ्रॉविंग अपवादों के बारे में कुछ मार्गदर्शन है। वहां वे राज्य करते हैं:
यदि संभव हो तो नियंत्रण के सामान्य प्रवाह के अपवादों का उपयोग न करें।
पहला नोट "यदि संभव हो तो" की उदारता है। अधिक महत्वपूर्ण बात, विवरण इस संदर्भ देता है:
framework designers should design APIs so users can write code that does not throw exceptions
इसका मतलब यह है कि यदि आप एक एपीआई लिख रहे हैं जो किसी अन्य व्यक्ति द्वारा उपभोग किया जा सकता है, तो उन्हें बिना किसी प्रयास या पकड़ के अपवाद को नेविगेट करने की क्षमता दें। उदाहरण के लिए, अपने अपवाद-फेंकने वाले पार्स विधि के साथ एक TryParse प्रदान करें। हालांकि यह कहीं नहीं कहता है कि आपको अपवाद नहीं पकड़ना चाहिए।
इसके अलावा, जैसा कि एक अन्य उपयोगकर्ता बताते हैं, कैच ने हमेशा प्रकार से फ़िल्टरिंग की अनुमति दी है और हाल ही में जब क्लॉज के माध्यम से आगे फ़िल्टरिंग की अनुमति देते हैं । यह भाषा सुविधाओं की बर्बादी की तरह लगता है अगर हम उनका उपयोग करने वाले नहीं हैं।
यह कहा जा सकता है कि एक फेंके गए अपवाद के लिए कुछ लागत है, और एक भारी लूप में MAY प्रभाव प्रदर्शन की लागत है। हालांकि, यह भी कहा जा सकता है कि "कनेक्टेड एप्लिकेशन" में एक अपवाद की लागत नगण्य होने वाली है। वास्तविक लागत की एक दशक पहले जांच की गई थी: https://stackoverflow.com/a/891230/852208
दूसरे शब्दों में, एक डेटाबेस के कनेक्शन और क्वेरी की लागत एक फेंके गए अपवाद को बौना करने की संभावना है।
वह सब एक तरफ, मैं यह निर्धारित करना चाहता था कि वास्तव में कौन सी विधि तेज है। जैसा कि अपेक्षित था, कोई ठोस जवाब नहीं है।
कोई भी कोड जो स्तंभों पर लूप करता है, वह स्तंभों की संख्या के रूप में धीमा हो जाता है। यह भी कहा जा सकता है कि अपवादों पर भरोसा करने वाला कोई भी कोड उस दर के आधार पर धीमा हो जाएगा जिसमें क्वेरी नहीं मिल रही है।
चाड ग्रांट और मैट हैमिल्टन दोनों के जवाबों को लेते हुए, मैंने दोनों विधियों को 20 कॉलम तक और 50% त्रुटि दर तक चलाया (ओपी ने संकेत दिया कि वह इस दो परीक्षण का उपयोग विभिन्न प्रॉप्स के बीच कर रहे थे, इसलिए मैंने दो के रूप में कुछ मान लिया) ।
यहाँ परिणाम हैं, LinqPad के साथ साजिश रची:
यहां के ज़िगज़ैग प्रत्येक कॉलम की गिनती में दोष दर (कॉलम नहीं मिला) हैं।
संकरा परिणाम सेट पर, लूपिंग एक अच्छा विकल्प है। हालाँकि, GetOrdinal / Exception विधि स्तंभों की संख्या के प्रति लगभग संवेदनशील नहीं है और लगभग 11 स्तंभों से लूपिंग विधि को बेहतर बनाने के लिए शुरू होता है।
कहा कि मेरे पास वास्तव में एक वरीयता प्रदर्शन बुद्धिमान नहीं है क्योंकि 11 कॉलम उचित लगते हैं क्योंकि एक पूरे आवेदन पर औसतन कॉलम की संख्या वापस आती है। या तो मामले में हम यहाँ एक मिलीसेकंड के अंशों के बारे में बात कर रहे हैं।
हालाँकि, एक कोड सादगी पहलू, और अन्य समर्थन से, मैं शायद गेटऑर्डिनल मार्ग के साथ जाऊँगा।
यहाँ linqpad रूप में परीक्षण किया गया है। अपने तरीके से प्रजनन करने के लिए स्वतंत्र महसूस करें:
void Main()
{
var loopResults = new List<Results>();
var exceptionResults = new List<Results>();
var totalRuns = 10000;
for (var colCount = 1; colCount < 20; colCount++)
{
using (var conn = new SqlConnection(@"Data Source=(localdb)\MSSQLLocalDb;Initial Catalog=master;Integrated Security=True;"))
{
conn.Open();
//create a dummy table where we can control the total columns
var columns = String.Join(",",
(new int[colCount]).Select((item, i) => $"'{i}' as col{i}")
);
var sql = $"select {columns} into #dummyTable";
var cmd = new SqlCommand(sql,conn);
cmd.ExecuteNonQuery();
var cmd2 = new SqlCommand("select * from #dummyTable", conn);
var reader = cmd2.ExecuteReader();
reader.Read();
Func<Func<IDataRecord, String, Boolean>, List<Results>> test = funcToTest =>
{
var results = new List<Results>();
Random r = new Random();
for (var faultRate = 0.1; faultRate <= 0.5; faultRate += 0.1)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var faultCount=0;
for (var testRun = 0; testRun < totalRuns; testRun++)
{
if (r.NextDouble() <= faultRate)
{
faultCount++;
if(funcToTest(reader, "colDNE"))
throw new ApplicationException("Should have thrown false");
}
else
{
for (var col = 0; col < colCount; col++)
{
if(!funcToTest(reader, $"col{col}"))
throw new ApplicationException("Should have thrown true");
}
}
}
stopwatch.Stop();
results.Add(new UserQuery.Results{
ColumnCount = colCount,
TargetNotFoundRate = faultRate,
NotFoundRate = faultCount * 1.0f / totalRuns,
TotalTime=stopwatch.Elapsed
});
}
return results;
};
loopResults.AddRange(test(HasColumnLoop));
exceptionResults.AddRange(test(HasColumnException));
}
}
"Loop".Dump();
loopResults.Dump();
"Exception".Dump();
exceptionResults.Dump();
var combinedResults = loopResults.Join(exceptionResults,l => l.ResultKey, e=> e.ResultKey, (l, e) => new{ResultKey = l.ResultKey, LoopResult=l.TotalTime, ExceptionResult=e.TotalTime});
combinedResults.Dump();
combinedResults
.Chart(r => r.ResultKey, r => r.LoopResult.Milliseconds * 1.0 / totalRuns, LINQPad.Util.SeriesType.Line)
.AddYSeries(r => r.ExceptionResult.Milliseconds * 1.0 / totalRuns, LINQPad.Util.SeriesType.Line)
.Dump();
}
public static bool HasColumnLoop(IDataRecord dr, string columnName)
{
for (int i = 0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
public static bool HasColumnException(IDataRecord r, string columnName)
{
try
{
return r.GetOrdinal(columnName) >= 0;
}
catch (IndexOutOfRangeException)
{
return false;
}
}
public class Results
{
public double NotFoundRate { get; set; }
public double TargetNotFoundRate { get; set; }
public int ColumnCount { get; set; }
public double ResultKey {get => ColumnCount + TargetNotFoundRate;}
public TimeSpan TotalTime { get; set; }
}