Повышение эффективности для этого кода обработки текста - PullRequest
3 голосов
/ 27 декабря 2010

Я пишу программу, которая подсчитывает количество слов в текстовом файле, который уже находится в нижнем регистре и разделен пробелами. Я хочу использовать словарь и считать только слово, если оно в словаре. Проблема в том, что словарь довольно большой (~ 100 000 слов), а в каждом текстовом документе также ~ 50 000 слов. Таким образом, коды, которые я написал ниже, становятся очень медленными (около 15 секунд для обработки одного документа на компьютере с четырьмя i7). Мне интересно, что-то не так с моим кодированием и можно ли повысить эффективность программы. Большое спасибо за Вашу помощь. Код ниже:

public static string WordCount(string countInput)
        {
            string[] keywords = ReadDic(); /* read dictionary txt file*/

            /*then reads the main text file*/
            Dictionary<string, int> dict = ReadFile(countInput).Split(' ')
                .Select(c => c)
                .Where(c => keywords.Contains(c))
                .GroupBy(c => c)
                .Select(g => new { word = g.Key, count = g.Count() })
                .OrderBy(g => g.word)
                .ToDictionary(d => d.word, d => d.count);

            int s = dict.Sum(e => e.Value);
            string k = s.ToString();
            return k;

        } 

Ответы [ 4 ]

7 голосов
/ 27 декабря 2010

Вы можете значительно повысить производительность, читая текстовый файл по одной строке за раз вместо создания огромной строки.

Можно звонить

File.ReadLines(path).SelectMany(s => s.Split(' '))

Не звонить ReadAllLines;потребуется построить огромный массив.


Ваш первый Select вызов совершенно бесполезен.


Ваш Contains вызов будет зацикленчерез весь словарь для каждого слова в файле.
Таким образом, вызов Where является операцией O (n 2 ).

Измените keywords на HashSet<string>.
Поскольку HashSets можно искать в постоянное время, вызов Where станет операцией O (n), что намного лучше.


Ваш второй вызов Select может бытьв сочетании с GroupBy, который сократит большое количество объектов:

 .GroupBy(c => c, (word, set) => new { word, count = set.Count() })

Словари неупорядоченно неупорядочены, поэтому ваш вызов OrderBy - бесполезная трата времени.

1 голос
/ 27 декабря 2010

Поскольку вы работаете с четырехъядерным процессором, вы можете по крайней мере добавить туда AsParallel().

1 голос
/ 27 декабря 2010

Как я вижу, весь ваш код можно заменить на

return ReadFile(countInput).Split(' ').Count(c => keywords.Contains(c));

И, как сказал SLaks , HashSet - улучшит производительность.
Еще одно улучшение: если вы вызываете этот код в цикле, вы не должны читать ReadDic() на каждой итерации - загрузите его один раз и передайте в качестве параметра.

0 голосов
/ 27 декабря 2010

Попробуйте изменить string[] keywords на HashSet<string> keywords. Ваш вызов «Contains» по сути является циклом, который будет значительно медленнее, чем поиск по хеш-ключу.

Если вы хотите по-настоящему модно, вы можете использовать несколько потоков, используя некоторые PLINQ , но я хотел бы убедиться, что вы оптимизировали производительность одного потока перед тем, как идти по этому пути.

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