Метод LINQ для добавления объектов к словарю

Я пытаюсь узнать немного больше о LINQ путем реализации орфографического корректора Peter Norvig в C#.

Первая часть включает взятие большого файла слов (приблизительно 1 миллион) и помещение его в словарь где key слово и value количество случаев.

Я обычно делал бы это как так:

foreach (var word in allWords)                                                    
{           
    if (wordCount.ContainsKey(word))
        wordCount[word]++;
    else
        wordCount.Add(word, 1);
}

Где allWords IEnumerable

В LINQ я в настоящее время делаю его как это:

var wordCountLINQ = (from word in allWordsLINQ
                         group word by word
                         into groups
                         select groups).ToDictionary(g => g.Key, g => g.Count());  

Я сравниваю эти 2 словаря путем рассмотрения весь и они идентичны, таким образом, они приводят к тем же результатам.

foreach цикл берет 3.82 secs, и запрос LINQ берет 4.49 secs

Я синхронизирую его с помощью класса Секундомера, и я работаю в режиме 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();
    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);                     
}

9
задан Matt Warren 26 April 2012 в 16:27
поделиться

2 ответа

Одной из причин, по которой версия LINQ медленнее, является то, что вместо одного словаря создаются два словаря:

  1. (внутренне) из группы по оператору; Группа также хранит каждое отдельное слово. Вы можете проверить это, глядя на Toarray (), а не счетчик (). Это много наверху, вам на самом деле не нужно в вашем случае.

  2. Метод для того, чтобы «Метод» в основном является FOREAFEAL над фактическим запросом LINQ, где результаты запроса добавляются в новый словарь. В зависимости от количества уникальных слов, это также может занять некоторое время.

Еще одна причина, что запрос LINQ немного медленнее, является то, что LINQ полагается на выражения лямбда (делегат в ответе Датана), и вызов делегата добавляет крошечное количество накладных расходов по сравнению с встроенным кодом.

Отредактируйте: Обратите внимание, что для некоторых сценариев LINQ (например, linq к 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);
6
ответ дан 4 December 2019 в 23:06
поделиться

Когда я создаю свой второй пример, а затем откройте его в разборке рефлектора, я получаю следующее:

Dictionary<string, int> wordCountLINQ = allWordsLINQ.GroupBy<string, string>(delegate (string word) {
    return word;
}).Select<IGrouping<string, string>, IGrouping<string, string>>(delegate (IGrouping<string, string> groups) {
    return groups;
}).ToDictionary<IGrouping<string, string>, string, int>(delegate (IGrouping<string, string> g) {
    return g.Key;
}, delegate (IGrouping<string, string> g) {
    return g.Count<string>();
});

, вероятно, он занимает больше времени, потому что существует больше функционирует вызовы, и в течение миллиона итераций, которые добавляют Отказ

1
ответ дан 4 December 2019 в 23:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: