TLDR:
কর্মক্ষমতা এবং খারাপ অনুশীলন সম্পর্কে দাবী সহ প্রচুর উত্তর, তাই আমি এখানে এটি স্পষ্ট করে বলছি।
ব্যতিক্রমী রুট উচ্চতর সংখ্যক ফিরে আসা কলামগুলির জন্য দ্রুত, লুপের রুটটি কম সংখ্যক কলামগুলির জন্য দ্রুত এবং ক্রসওভার পয়েন্টটি 11 টি কলামের কাছাকাছি। একটি গ্রাফ এবং পরীক্ষার কোডটি দেখতে নীচে স্ক্রোল করুন।
পুরো উত্তর:
শীর্ষস্থানীয় কয়েকটি উত্তরের কোড কাজ করে, তবে যুক্তিতে ব্যতিক্রম হ্যান্ডলিংয়ের গ্রহণযোগ্যতার উপর ভিত্তি করে "আরও ভাল" উত্তরের জন্য এখানে অন্তর্নিহিত বিতর্ক রয়েছে এবং এটি সম্পর্কিত পারফরম্যান্স।
এটিকে পরিষ্কার করতে, আমি বিশ্বাস করি না ক্যাচিং ব্যতিক্রম সম্পর্কিত অনেক গাইডেন্স আছে। মাইক্রোসফ্টের থ্রোং ব্যতিক্রম সম্পর্কিত কিছু গাইডেন্স রয়েছে । সেখানে তারা বলে:
সম্ভব হলে নিয়ন্ত্রণের স্বাভাবিক প্রবাহের জন্য ব্যতিক্রম ব্যবহার করবেন না।
প্রথম নোটটি হ'ল "যদি সম্ভব হয়" এর লেন্সি। আরও গুরুত্বপূর্ণ, বর্ণনাটি এই প্রসঙ্গে দেয়:
framework designers should design APIs so users can write code that does not throw exceptions
এর অর্থ হ'ল আপনি যদি এমন একটি এপিআই লিখছেন যা অন্য কারও দ্বারা গ্রাস করা হতে পারে তবে চেষ্টা / ধরা ছাড়াই একটি ব্যতিক্রম নেভিগেট করার ক্ষমতা দিন। উদাহরণস্বরূপ, আপনার ব্যতিক্রম-ছোঁড়া পার্স পদ্ধতি সহ একটি ট্রিপ পার্স সরবরাহ করুন। এটি কোথাও বলছে না যদিও আপনার কোনও ব্যতিক্রম ধরা উচিত নয়।
তদ্ব্যতীত, অন্য একজন ব্যবহারকারী উল্লেখ করেছেন, ক্যাচগুলি সর্বদা টাইপ অনুসারে ফিল্টারিংয়ের অনুমতি দেয় এবং কিছুটা সাম্প্রতিক সময়ে যখন ক্লজটির মাধ্যমে আরও ফিল্টারিংয়ের অনুমতি দেয় । এটি ভাষা বৈশিষ্ট্যগুলির অপব্যয়ের মতো মনে হয় যদি আমরা সেগুলি ব্যবহার না করে।
এটি বলা যেতে পারে যে একটি নিক্ষিপ্ত ব্যতিক্রমের জন্য কিছু ব্যয় রয়েছে এবং এটি একটি ভারী লুপের জন্য প্রভাব ফেলতে পারে প্রভাবিত। তবে এটিও বলা যেতে পারে যে একটি "সংযুক্ত অ্যাপ্লিকেশন" এ ব্যতিক্রমের ব্যয়টি নগণ্য হতে চলেছে। প্রকৃত ব্যয় এক দশক আগে তদন্ত করা হয়েছিল: https://stackoverflow.com/a/891230/852208
অন্য কথায়, একটি ডাটাবেসের সংযোগ এবং ক্যোয়ারির ব্যয় একটি ছোঁড়া ব্যতিক্রমের বামন হতে পারে।
সব কিছু বাদ দিয়ে, আমি নির্ধারণ করতে চেয়েছিলাম কোন পদ্ধতিটি সত্যই দ্রুততর। যেমনটি প্রত্যাশা করা হয়েছে তেমন কোনও ठोस উত্তর নেই।
যে কোনও কোড কলামগুলির উপরে লুপ করে সেগুলি কলামের সংখ্যা বিদ্যমান থাকায় ধীর হয়ে যায়। এটিও বলা যেতে পারে যে ব্যতিক্রমগুলির উপর নির্ভর করে যে কোনও কোড কোয়েরিটি ব্যর্থ হওয়ার হারের উপর নির্ভর করে ধীর হয়ে যাবে।
চ্যাড গ্রান্ট এবং ম্যাট হ্যামিল্টন উভয়ের উত্তর গ্রহণ করে, আমি 20 টি কলাম এবং 50% ত্রুটি হার পর্যন্ত দুটি পদ্ধতি চালিয়েছি (ওপি ইঙ্গিত করেছে যে তিনি এই দুটি পরীক্ষা বিভিন্ন প্রকল্পের মধ্যে ব্যবহার করছেন, তাই আমি ধরে নিলাম দু'জন হিসাবে) ।
লিনকপ্যাডের মাধ্যমে প্লট করা ফলাফলগুলি এখানে:
এখানে জিগজাগগুলি প্রতিটি কলাম গণনার মধ্যে ত্রুটি হার (কলাম পাওয়া যায় নি)।
সংকীর্ণ ফলাফল সেট ওভার, লুপিং একটি ভাল পছন্দ। তবে, গেটঅর্ডিনাল / ব্যাতিক্রম পদ্ধতিটি কলামগুলির সংখ্যার তুলনায় প্রায় সংবেদনশীল নয় এবং প্রায় 11 টি কলামের লুপিং পদ্ধতিটিকে ছাড়িয়ে যেতে শুরু করে।
এটি বলেছে যে আমার কাছে 11 টি কলাম যুক্তিযুক্ত বলে মনে হচ্ছে কারণ পুরো অ্যাপ্লিকেশনটিতে গড় সংখ্যক কলাম ফিরে এসেছে। উভয় ক্ষেত্রেই আমরা এখানে মিলিসেকেন্ডের ভগ্নাংশের কথা বলছি।
তবে একটি কোড সরলতার দিক এবং উপাধি সমর্থন থেকে আমি সম্ভবত গেটআর্ডিনাল রুট দিয়ে যেতে চাই।
লিনকপ্যাড আকারে পরীক্ষাটি এখানে। আপনার নিজস্ব পদ্ধতিতে পুনরায় পোস্ট করতে নির্দ্বিধায়:
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; }
}