Медленно работает парсер файла


У меня есть метод, который парсит файл. Беру все свои слова и добавлять их в набор. Каждое слово содержит список строк , которые содержат слово. Слова, не строки, А класс, который я создал:

class Word : IComparable<Word>
{
    public Word()
    {
        Lines = new List<Line>();
    }
    public string WordStr { get; set; }
    public List<Line> Lines { get; set; }

    public int CompareTo(Word other)
    {
        return string.Compare(this.WordStr, other.WordStr, StringComparison.OrdinalIgnoreCase);
    }
}

И метод, который парсит файл (я подозреваю, что я не делаю что-то правильно здесь):

private void AddFile(string path)
    {
        Regex regex = new Regex("[^A-Za-z0-9\\s\\']");
        FileInfo fi = new FileInfo(path);
        if (!fi.Exists || Files.Contains(path.ToLower())) //File does not exist or file already indexed
        {
            return;
        }
        Files.Add(path.ToLower());
        StreamReader sr = new StreamReader(path);
        string file = sr.ReadToEnd();
        string saniFile = regex.Replace(file, "");
        string[] saniLines = saniFile.Split(new char[]{'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
        int lineNo = 1;
        foreach (var l in saniLines)
        {
            Line line = new Line(l, path, lineNo);
            string[] words = l.Split(' ');
            foreach (var word in words)
            {
                Word w = new Word();
                w.WordStr = word;

                if (Words.Contains(w, new WordComparer())) //Set already contains the word
                {
                    Word wordToAdd = (from wors in Words where wors.WordStr.ToLower() == w.WordStr.ToLower() select wors).First();
                    if (!wordToAdd.Lines.Contains(line))
                        wordToAdd.Lines.Add(line);
                }
                else
                {
                    w.Lines.Add(line);
                    Words.Add(w);
                }
            }
            lineNo++;
        }
    }

У меня точно такой же функциональностью работает в C++ и порядков величины быстрее. Так что-то я делаю неправильно? Что, если я использовал словаре sorteddictionary , а не набор слов? Тогда ключ может быть строкой, которая является словом и значением будет список строк, которые содержат это слово.

Для справки, текстовый файл 618KB занимает несколько секунд для анализа и указатель в C++. Это заняло у меня минуты, чтобы сделать это в C#.



1739
7
задан 5 мая 2011 в 11:05 Источник Поделиться
Комментарии
2 ответа

Ваши инстинкты в основном правильно, что может, словарь какой-то мог помочь. Давайте посмотрим на несколько ключевых строк кода.

foreach (var l in saniLines)
{
Line line = new Line(l, path, lineNo);
string[] words = l.Split(' ');
foreach (var word in words)
{
Word w = new Word();
w.WordStr = word;

if (Words.Contains(w, new WordComparer())) //Set already contains the word
{
Word wordToAdd = (from wors in Words where wors.WordStr.ToLower() == w.WordStr.ToLower() select wors).First();
if (!wordToAdd.Lines.Contains(line))
wordToAdd.Lines.Add(line);

Я вижу 5 вложенных циклов здесь. Если вы шаг через код, вы их увидите тоже. Внешний 2 очевидны, ты явно их закодировал. Но шагая через код откроет цикл делается на слова.Содержит. Запрос LINQ-это также абстрактный петли. И wordToAdd.Линии.Содержит также будет цикл. Ты заплатишь высокую цену за это.

Профилирования всегда поможет. Но я бы предложил, возможно, изменяя слова в какой-то класс IDictionary, где вы можете заменить

if (Words.Contains(w, new WordComparer())) //Set already contains the word
{
Word wordToAdd = (from wors in Words where wors.WordStr.ToLower() == w.WordStr.ToLower() select wors).First();

Что-то более похожее

if (Words.ContainsKey(w.WordStr))
{
Word wordToAdd = Words[w.WordStr];
// ...
}

И вы могли бы также изменить линии в поиска HashSet вместо списка. Это должно улучшить производительность

if (!wordToAdd.Lines.Contains(line))
wordToAdd.Lines.Add(line);

На самом деле, для поиска HashSet.Добавить(Т вал) может быть использован сам по себе, без вызова содержит, как добавить возвращает значение bool , указывающее, если в дополнение может быть выполнена. Если соответствующее значение уже есть в наборе, он просто возвращает значение false без изменения его содержания.

Попробуйте эти изменения, измерить производительность до и после и увидеть, где вы находитесь.

Несвязанные, но я также рекомендую вам обернуть ваш поток streamreader в с помощью () { } блок, так что ресурс утилизируются должным образом, когда вы живете с этим.

14
ответ дан 6 мая 2011 в 02:05 Источник Поделиться

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

3
ответ дан 6 мая 2011 в 12:05 Источник Поделиться