Как загрузить файл со словами в список, где в файле более 3 миллионов строк

Можно ли загрузить файл с 3 или 4 миллионами строк менее чем за 1 секунду (1.000000)? В одной строке содержится одно слово. Слова могут иметь длину от 1 до 17 (это имеет значение?).

Мой код сейчас:

List<string> LoadDictionary(string filename)
{
    List<string> wordsDictionary = new List<string>();

    Encoding enc = Encoding.GetEncoding(1250);//I need ę ą ć ł etc.
    using (StreamReader r = new StreamReader(filename, enc))
    {
        string line = "";
        while ((line = r.ReadLine()) != null)
        {
            if (line.Length > 2)
            {
                wordsDictionary.Add(line);
            }
        }
    }

    return wordsDictionary;
}

Результаты рассчитанного исполнения:

время загрузки 4 миллионов слов - результат изображения

Как я могу заставить метод выполнить его вдвое быстрее?


person deadfish    schedule 08.11.2011    source источник
comment
У вас есть доступ к профилировщику? Не зная, где находится узкое место (дисковый ввод-вывод, накладные расходы на вызов функций, управление памятью и т. Д.), Трудно понять, как его ускорить.   -  person Adrian McCarthy    schedule 09.11.2011
comment
Вы можете написать варианты, чтобы увидеть, где находится узкое место. Можете ли вы просто прочитать файл менее чем за секунду, не добавляя слов? Можно ли добавить в словарь три или четыре миллиона слов, не читая их из файла?   -  person Adrian McCarthy    schedule 09.11.2011
comment
@AdrianMcCarthy с заявленной мощностью new List<string>( 3500000 ) (3 миллиона с половиной) весь процесс занимает почти 1 секунду. См. Ответ под @ brian519. Без wordsDictionary.Add(line); время: 0.4848208   -  person deadfish    schedule 09.11.2011


Ответы (4)


Если вы знаете, что ваш список будет большим, вам следует установить хорошую стартовую вместимость.

List<string> wordsDictionary = new List<string>( 100000 );

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

person brian519    schedule 08.11.2011
comment
+1: Поскольку он знает, что его диапазон будет от трех до четырех миллионов, ему, вероятно, следует дать ему начальную емкость в миллионы. - person StriplingWarrior; 09.11.2011
comment
К вашему сведению: я просто попробовал это на некоторых образцах данных, и эффект был незначительным. Ввод-вывод здесь действительно самая медленная точка. - person StriplingWarrior; 09.11.2011
comment
Это хорошее предположение, но без профилирования трудно понять, является ли это узким местом. - person Adrian McCarthy; 09.11.2011
comment
@ brian519 после объявления размера до 3 с половиной миллионов - изображение Теперь он дает 1.0160538: ) Хорошо, я не понимал, что это может сделать это быстрее. - person deadfish; 09.11.2011
comment
Хорошая точка зрения. Я поэкспериментировал: на моей машине примерно 1/3 времени тратится на чтение данных из файла. 2/3 - это установка значений в массиве. ЦП почти не вздрагивает. Так что ОЗУ, очевидно, играет большую роль, чем я думал. - person StriplingWarrior; 09.11.2011

Как работает File.ReadAllLines() и некоторые LINQ?

public List<string> LoadDictionary(string filename)
{
    List<string> wordsDictionary = new List<string>();
    Encoding enc = Encoding.GetEncoding(1250);
    string[] lines = File.ReadAllLines(filename,enc);
    wordsDictionary.AddRange(lines.Where(x => x.Length > 2));
    return wordsDictionary;
}
person p.campbell    schedule 09.11.2011
comment
он возвращает 1.1970612, он медленнее моего. Но +1 для LINQ :) - person deadfish; 09.11.2011
comment
@ Cooldown4seconds, насколько он меняется при 2-м, 3-м и n-м прогоне? то есть я думаю, что мы имеем дело с диском, который является узким местом. У вас есть SSD, на котором вы можете запустить это, например? - person p.campbell; 09.11.2011
comment
В моем тестировании File.ReadAllLines сам по себе занимает около 90% времени, чем исходный метод. Как только вы добавите AddRange, все будет так же. Однако хорошее предложение: это намного чище. - person StriplingWarrior; 09.11.2011
comment
@ p.campbell Я использую Intel Core 2 Quad CPU Q6600 @ 2.40GHz 2.40GHz, Installed memory (RAM) 8GB, System type x64 W7. Следующие результаты: 1.1940683 1.1814641 1.1924561. Однако это приложение должно работать на школьных компьютерах медленнее, чем у меня. У них нет x64. - person deadfish; 09.11.2011
comment
@ Cooldown4seconds ОК, это круто. Диск - это узкое место; ЦП и памяти не так уж много. - person p.campbell; 09.11.2011

На данный момент ваш самый большой удар по производительности, вероятно, связан с извлечением данных с жесткого диска в память. Маловероятно, что вы сможете что-то сделать, чтобы заставить его работать намного быстрее, если не считать более качественное оборудование.

person StriplingWarrior    schedule 08.11.2011

Профиль. Профиль. Профиль.

Мы все можем угадать, на что тратится время, а затем предложить другие методы, которые могут быть быстрее. У некоторых из нас может быть даже хорошая интуиция, или им повезет, и они наткнутся на правильный ответ. Но гораздо продуктивнее будет измерить, повторить и снова измерить.

Рэймонд Чен сделал интересную серию статей о загрузке китайско-английского словаря и быстро загружается. Это не совсем то же самое (он выполняет преобразование символов и некоторый простой синтаксический анализ, а словарь был немного меньше), и это на другом языке. Но я все равно рекомендую серию, потому что она показывает правильный способ оптимизации чего-то вроде этого: профиль, профиль, профиль.

person Adrian McCarthy    schedule 09.11.2011
comment
@Cooldown 4 секунды: профилировщик - это инструмент, который наблюдает за работой вашей программы и сообщает вам, какие части кода занимают больше всего времени. При оптимизации нужно знать, какие части медленные. Если 90% времени тратится на выделение памяти для списка, но вы работаете над ускорением доступа к диску, то вы зря тратите свои усилия. Профилирование помогает не тратить зря время, позволяя сосредоточиться на важной части. - person Adrian McCarthy; 11.11.2011