Dot-Net
將項目添加到字典的 LINQ 方法
我試圖通過在 C# 中實現 Peter Norvig 的拼寫校正器來了解更多關於 LINQ 的知識。
第一部分涉及獲取一個大的單詞文件(大約 100 萬個)並將其放入字典中,其中
key是單詞,並且value是出現次數。我通常會這樣做:
foreach (var word in allWords) { if (wordCount.ContainsKey(word)) wordCount[word]++; else wordCount.Add(word, 1); }
allWords在哪裡IEnumerable<string>在 LINQ 中,我目前正在這樣做:
var wordCountLINQ = (from word in allWordsLINQ group word by word into groups select groups).ToDictionary(g => g.Key, g => g.Count());我通過查看所有字典來比較這兩個字典,
<key, value>它們是相同的,所以它們產生了相同的結果。
foreach循環耗時3.82 秒,LINQ 查詢耗時4.49 秒我正在使用 Stopwatch 類對其進行計時,並且我正在 RELEASE 模式下執行。我不認為表現不好我只是想知道是否有差異的原因。
我是在以低效的方式執行 LINQ 查詢還是遺漏了什麼?
**更新:**這是完整的基準程式碼範例:
public static void TestCode() { //File can be downloaded from http://norvig.com/big.txt and consists of about a million words. const string fileName = @"path_to_file"; var allWords = from Match m in Regex.Matches(File.ReadAllText(fileName).ToLower(), "[a-z]+", RegexOptions.Compiled) select m.Value; var wordCount = new Dictionary<string, int>(); var timer = new Stopwatch(); timer.Start(); foreach (var word in allWords) { if (wordCount.ContainsKey(word)) wordCount[word]++; else wordCount.Add(word, 1); } timer.Stop(); Console.WriteLine("foreach loop took {0:0.00} ms ({1:0.00} secs)\n", timer.ElapsedMilliseconds, timer.ElapsedMilliseconds / 1000.0); //Make LINQ use a different Enumerable (with the exactly the same values), //if you don't it suddenly becomes way faster, which I assmume is a caching thing?? var allWordsLINQ = from Match m in Regex.Matches(File.ReadAllText(fileName).ToLower(), "[a-z]+", RegexOptions.Compiled) select m.Value; timer.Reset(); timer.Start(); var wordCountLINQ = (from word in allWordsLINQ group word by word into groups select groups).ToDictionary(g => g.Key, g => g.Count()); timer.Stop(); Console.WriteLine("LINQ took {0:0.00} ms ({1:0.00} secs)\n", timer.ElapsedMilliseconds, timer.ElapsedMilliseconds / 1000.0); }
LINQ 版本較慢的原因之一是因為創建了兩個字典而不是一個字典:
- (內部)來自 group by 運算符;group by 還儲存每個單詞。您可以通過查看 ToArray() 而不是 Count() 來驗證這一點。在您的情況下,這是您實際上不需要的很多成本。
- ToDictionary 方法基本上是對實際 LINQ 查詢的 foreach,其中查詢的結果被添加到新字典中。根據唯一詞的數量,這也可能需要一些時間。
LINQ 查詢稍慢的另一個原因是因為 LINQ 依賴於 lambda 表達式(Dathan 的答案中的委託),並且與內聯程式碼相比,呼叫委託會增加少量成本。
**編輯:**請注意,對於某些 LINQ 方案(例如 LINQ to SQL,但不是記憶體中的 LINQ,例如此處),重寫查詢會產生更優化的計劃:
from word in allWordsLINQ group word by word into groups select new { Word = groups.Key, Count = groups.Count() }但是請注意,這並沒有給你一個字典,而是一個單詞序列和它們的計數。您可以將其轉換為字典
(from word in allWordsLINQ group word by word into groups select new { Word = groups.Key, Count = groups.Count() }) .ToDictionary(g => g.Word, g => g.Count);