最近參加了SkillTree的課程,不想讓這些學習效果白白浪費,所以決定把這幾天所學到的東西寫下來。
LINQ 並不會讓程式變快 只會讓你寫程式變快
基本上效能的表現關係如右: For>=Foreach>Where
- For 及 Foreach基本上無太大差異(Compiler會把Foreach最佳化成For)
- For可用在非Enumerble的元素集; Foreach用在Enumerble的元素集會更好懂
- Where會變慢原因: Delegate的呼叫、使用者寫法問題
LINQ常見的誤用
- LINQ大約只有一半的函示延遲值執行(Deferred Execution: https://dotblogs.com.tw/hatelove/2013/09/10/csharp-linq-deferred-execution-introduction)
- 另一半則是即刻查詢的函示(要小心)
- LastOrDefault: Traverse至最後一個元素(等同於Traverse全部)
- OrderBy, Reverse: 針對所有元素做處理
- ToList, ToLookup, ToDictionary: 針對所查詢的元素們做複製的動作
- All, Min, Max, Sum, Count: 所有元素都必須參與比較
LINQ不必要的查詢
public void MultipleQuery()
{
var sw = new Stopwatch();
var data = Enumerable.Range(1, 5000000).ToList();
data.Add(5000003);
sw.Start();
var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);
var target = filters.FirstOrDefault(a => a == 5000001);
if (target == 0)
target = filters.FirstOrDefault(a => a == 5000002);
if (target == 0)
target = filters.FirstOrDefault(a => a == 5000003);
sw.Stop();
Console.WriteLine("Without ToList:" + sw.ElapsedMilliseconds + "(ms)");
}
public void MultipleQuery2()
{
var sw = new Stopwatch();
var data = Enumerable.Range(1, 5000000).ToList();
data.Add(5000003);
sw.Start();
var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003).ToList();
var target = filters.FirstOrDefault(a => a == 5000001);
if (target == 0)
target = filters.FirstOrDefault(a => a == 5000002);
if (target == 0)
target = filters.FirstOrDefault(a => a == 5000003);
sw.Stop();
Console.WriteLine("With ToList:" + sw.ElapsedMilliseconds+"(ms)");
}
執行結果秒數
Without ToList:93(ms)
With ToList:40(ms)
Without ToList:94(ms)
With ToList:30(ms)
Without ToList:83(ms)
With ToList:32(ms)
MultiQuery
var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);
MultiQuery2
var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003).ToList();
可以看到其實只差在有沒有在結尾加上ToList()。
會造成這樣的原因就是我們上面所提到的ToList()是屬於即刻查詢的一種
當你加上他的時候此時在filters內的就只有{5000001, 5000002, 5000003}
等到後面要執行
var target = filters.FirstOrDefault(a => a == 5000001);
我們只需要針對{5000001, 5000002, 5000003}去做where即可,大大地縮小我們所需要查詢的空間大小。
那沒有加上ToList()是怎麼回事呢? 相信你也猜到了,因為這個運算是屬於延遲執行的。
所以當程式碼執行到
var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);
是不會馬上執行的,而是當我們執行到
var target = filters.FirstOrDefault(a => a == 5000001);
才會真正的對(a => a == 5000001 || a == 5000002 || a == 5000003)這個條件去做篩選,加上我們刻意把目標放在最後才讓程式碼找到,整整多了三次執行這個查詢條件,這也是造成效能大輸的原因阿~
今天所學的重點就是
- 必須明確分辨即刻查詢及延遲執行的差異
- 排序及搜尋還是要善用演算法,畢竟LINQ並不是以效能著名的工具
- 記得我們是讓寫程式變快,不是讓程式變快
PS: 本人只是一個新手,若有謬誤,煩請指教,謝謝。