【问题标题】:Improve Efficiency for This Text Processing Code提高此文本处理代码的效率
【发布时间】:2010-12-27 14:53:16
【问题描述】:

我正在编写一个程序来计算文本文件中的单词数,该文本文件已经是小写并用空格分隔。我想使用字典,并且只计算字典中的单词。问题是字典非常大(约 100,000 个单词),每个文本文档也有约 50,000 个单词。因此,我在下面编写的代码变得非常慢(在 quad i7 机器上处理一个文档大约需要 15 秒)。我想知道我的编码是否有问题,是否可以提高程序的效率。非常感谢你的帮助。代码如下:

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;

        } 

【问题讨论】:

  • 你为什么要制作字典?

标签: c# text


【解决方案1】:

您可以通过一次读取一行文本文件而不是构建一个巨大的字符串来极大地提高性能。

你可以打电话

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

请勿拨打ReadAllLines;它需要构建一个巨大的数组。


你的第一个Select 电话完全没用。


您的Contains 调用将在整个字典中循环查找文件中的每个单词。
因此,Where 调用是一个 O(n2) 操作。

keywords 更改为HashSet&lt;string&gt;
由于HashSets可以在恒定时间内被搜索到,所以Where的调用会变成O(n)的运算,这样会好很多。


您的第二个Select 调用可以与GroupBy 结合使用,这将减少大量的对象分配:

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

字典本质上是无序的,所以你的OrderBy 电话是在浪费时间。

【讨论】:

  • 关于为什么这会加快速度的描述会得到我的 +1。
  • 一定要喜欢 linq,让它很容易添加到那些 O(n) 中
  • @SLaks,感谢并强调,除了 HashSet 更改之外,其他任何更改都不会对大型 n 产生太大影响。
  • @johnv,要得到一个HashSet,最简单的重写是代码行是HashSet&lt;string&gt; keywords = new HashSet&lt;string&gt;(ReadDic());你也可以重构ReadDic函数本身。
  • @Anthony:更改ReadDic 以返回IEnumerable&lt;string&gt; 而不分配数组。 (或者,更好的是,直接返回 HashSet&lt;string&gt;
【解决方案2】:

由于您运行的是四核,您至少可以在其中输入AsParallel()

【讨论】:

    【解决方案3】:

    我可以看到你的所有代码都可以替换为

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

    而且,正如 SLaks 所说,HashSet - 会提高性能。
    另一个改进:如果您在循环中调用此代码,则不应在每次迭代时读取 ReadDic() - 加载一次并作为参数传递。

    【讨论】:

    • 他想要一本逐字词典。
    • @SLaks 在给定的代码中,他返回字数并且根本不使用(返回)字典。我哪里错了?
    【解决方案4】:

    尝试将string[] keywords 更改为HashSet&lt;string&gt; keywords。您对“包含”的调用本质上是一个循环,这将比通过哈希键查找要慢得多。

    如果你真的想变得花哨,你可以通过使用一些 PLINQ 来使用多线程,但我会确保你在走这条路之前已经优化了你的单线程性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-23
      • 2017-06-25
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 2010-10-27
      相关资源
      最近更新 更多