100 лучших значений в словареПочему LinQ намного быстрее, чем цикл foreach? - PullRequest
0 голосов
/ 08 января 2012

Я пишу простое приложение для разбора огромного текстового файла (60 ГБ) и сохранения всех слов и времени, в течение которого оно появляется в файле.Для тестирования я сократил файл до 2 ГБ.

У меня есть слова и цифры в словаре, хотя мне трудно поверить в результаты, которые я вижу.

Итогослов в словаре: 1128495

Код, который я использую:

sw.Start();

StringBuilder sb = new StringBuilder();
sb.AppendFormat("<html><head></head><body>");
lock (Container.values)
{
    int i = int.Parse(ctx.Request.QueryString["type"]);
    switch (i)
    {
        case 1: //LinQ
            var values = Container.values.OrderByDescending(a => a.Value.Count).Take(100);
            foreach (var value in values)
            {
                sb.AppendFormat("{0} - {1}<br />", value.Key, value.Value.Count);
            }
            break;
        case 2: //Foreach
            foreach (var y in Container.values)
            {

            }
            break;
        case 3: //For
            for (int x = 0; x < Container.values.Count; x++)
            {

            }
            break;
    }                
}
sw.Stop();
sb.AppendFormat("<br /><br /> {0}", sw.ElapsedMilliseconds);
sb.AppendFormat("</body>");

Запустил его дважды, скорости ниже в миллисекундах:

LinQ: # 1: 598,# 2 609

Foreach: # 1 1000, # 1020

Почему LinQ быстрее, чем foreach?Я предполагаю, что LinQ должен циклически проходить по самому Словарю, так как это происходит + сортировка всего этого своевременно?

Редактировать: После компиляции в режиме Release результаты выглядят следующим образом: LinQ: 796 (медленнее?) foreach: 945

Приложение представляет собой простое консольное приложение, код выполняется в HttpListener

Редактировать 2: Мне удалось выяснить, в чем проблема.Когда я инициализировал словарь, я установил его емкость равной 89000000 (в противном случае при обработке файла объемом 60 Гб будет выдано исключение OutOfMemory).По какой-то причине это резко замедляет работу цикла foreach.Если я установлю емкость на 1128495, цикл foreach выполняется за 56 миллисекунд.

Почему это происходит?Если я добавлю счетчик в цикл, он будет работать только 1128495 раз, даже с емкостью 89000000.

Ответы [ 2 ]

4 голосов
/ 08 января 2012

Цикл foreach реализуется компилятором, вызывая GetEnumerator (), а затем повторно вызывая MoveNext и Current для перечислителя.OrderByDescending LINQ обычно работает точно так же, он в основном выполняет foreach для извлечения всех элементов, а затем сортирует их.

Быстрый просмотр в ILSpy показывает, что OrderByDescending помещает контейнер во внутренний тип, называемый Buffer<T>, который имеет оптимизацию: в случае, если контейнер реализует ICollection<T>, он использует ICollection<T>.CopyTo вместо цикла foreach.Обычно OrderByDescending все равно не будет быстрее цикла foreach, потому что после извлечения элементов он должен их отсортировать.

Вы оставляете код в цикле foreach, код, который может объяснить, почему он медленнее?Если вы действительно используете пустой цикл foreach, возможно, объяснение состоит в том, что тип IEnumerator<T> (или метод GetEnumerator) Container.values медленнее, чем его метод CopyTo.

0 голосов
/ 08 января 2012

Ваша версия LINQ принимает только первые 100 элементов!

Удалите .Take(100), чтобы сравнить!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...