話說自從 Beta1 發布以來,關於 .Net Framework 3.5 的討論真是沸沸揚揚。我大概也算是比較早吃螃蟹的一位,Beta1 發布伊始就將它用於正式的開發中。雖然其間遇到過許許多多的 BUG,對於 .Net Framework 3.5 的核心特性 - LINQ,我個人感覺還是相當滿意的。然而僅僅是 LINQ 比較酷的語法,絕不足以決定項目所用的技術。事實上,如果不是我這種個人作坊式的開發,評估新技術是否采用很重要的一點,就是它的性能是否令人滿意。如果您還不了解 LINQ,可以通過簡單的 VB9 示例作出感性的認知。
LINQ 基礎示例
Dim Collection As String() = {"Beijing", "Shanghai", "Guangzhou", "Shenzhen", "Shijiazhuang", "Tianjin", "Taiyuan}
Console.WriteLine((From City In Collection Where City(0) = "T").First)
在此要強調一點,絕不要把 LINQ 與 Linq to SQL(DLINQ) 混為一談,DLINQ 僅僅是 LINQ 的一個應用,絕非 LINQ 的全部。與 LINQ 一同來到的是大量的編譯器特性,不論是 C#3 或是 VB9,都有著許多令人耳目一新的新語法特性。然而,由於 .Net Framework 3.x 都是 .Net Framework 2 的超集,所以所有的語法特性都不過是編譯時的翻譯。如果您試圖反編譯 LINQ 表達式,Lambda 或匿名函數,您將見到大批的函數嵌套,不知名的函數與其他令人頭昏腦漲的程序結構。這一切都是編譯器代為完成的。您無需擔心 C# 中的 Var,或是 VB 中的無需 As 的變量定義帶來的裝/拆箱問題,它們將在 IL 中被推斷為真實的數據類型。
探討 LINQ 的性能問題時,我們將不僅局限於簡單的時間比較,還將對 LINQ 的實現代碼進行簡單的分析,並就 Linq to SQL 生成的 T-SQL 語句討論優化數據庫性能的方法。工欲善其事,必先利其器,此系列中測試的平台基於 SQL Server 2005 SP2,反編譯工具采用 Lutz Roeder's Reflector。
言歸正傳。由於應用了擴展方法,LINQ 得以查詢任何現有 IEnumerable(Of T) 的內容。但是,性能問題卻被隱藏在通用性與易用性之下。讓我們做一個簡短的測試,測試是使用 LINQ 獲取一個隨機數字數組的開頭,結尾與總計,並與傳統方式按數組索引獲取作出對比。測試代碼如下:
Visual Basic 9 - LINQ 性能測試: 獲取隨機數組數據
Sub Main()
Dim Start As Integer
For Count As Integer = 0 To 2
Dim Random As New Random
Dim TempArray As New List(Of Integer)
'生成測試數組
For I As Integer = 0 To 50000
TempArray.Add(Random.Next(10000, 99999))
Next
'進行測試查詢
Dim Source As Integer() = TempArray.ToArray
'計算時間
'獲取最後一條
Start = Timer
Dim Last As Integer, First As Integer, Length As Integer
For I As Integer = 0 To 500000
Last = Source.Last
First = Source.First
Length = Source.Count
Next
System.Console.WriteLine("Linq 查詢數組首末與統計耗時: " & Math.Abs(Timer -Start) * 1000 & " 毫秒")
'方法2: 計算時間
Start = Timer
'獲取最後一條
Length = Source.Length - 1
For I As Integer = 0 To 500000
Last = Source(Length - 1)
First = Source(0)
Length = Source.Length
Next
System.Console.WriteLine("傳統查詢數組首末與統計耗時: " & Math.Abs(Timer - Start) * 1000 & " 毫秒")
Next
End Sub
程序運行於 Release 模式下,連續測試3次。測試結果如下:
Linq 查詢數組首末與統計耗時: 2109.375 毫秒
傳統查詢數組首末與統計耗時: 296.875 毫秒
Linq 查詢數組首末與統計耗時: 2406.25 毫秒
傳統查詢數組首末與統計耗時: 406.25 毫秒
Linq 查詢數組首末與統計耗時: 2156.25 毫秒
傳統查詢數組首末與統計耗時: 218.75 毫秒
看到這裡,通用的 LINQ 與傳統方法的差距就已明了。雖然僅僅是管中窺豹式的簡單測試,但各位若無必要,還是盡量使用原生於數組的功能。當然,各位看官欲知為何差距如此之大,且聽下回分解。水平有限,若有錯謬,敬請諒解並指出。