我正在编写一个程序来计算文本文件中的单词数,该单词已经是小写并用空格分隔。我想使用字典,只计算字典中的单词IF。问题是字典很大(~100万字),每个文本文件也有~5,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;
}
答案 0 :(得分:7)
通过一次读取一行文本文件而不是构建一个巨大的字符串,可以大大提高性能。
你可以打电话
File.ReadLines(path).SelectMany(s => s.Split(' '))
不要致电ReadAllLines
;它需要建立一个巨大的阵列。
您的第一次Select
来电完全无法使用
您的Contains
来电将循环播放文件中每个字词的整个词典
因此,Where
调用是O(n 2 )操作。
将keywords
更改为HashSet<string>
由于可以在恒定时间内搜索HashSet,Where
调用将成为O(n)操作,这样做要好得多。
您的第二个Select
来电可以与GroupBy
合并,这会削减大量的对象分配:
.GroupBy(c => c, (word, set) => new { word, count = set.Count() })
词典本质上是无序的,因此您的OrderBy
电话是无用的浪费时间。
答案 1 :(得分:1)
我可以看到所有代码都可以替换为
return ReadFile(countInput).Split(' ').Count(c => keywords.Contains(c));
而且,正如 SLaks 所说,HashSet - 会提高性能
还有一个改进:如果你在循环中调用这个代码,你不应该在每次迭代时读取ReadDic()
- 加载一次并作为参数传递。
答案 2 :(得分:1)
由于你正在运行四核,你至少可以在那里投入一个AsParallel()
。
答案 3 :(得分:0)
尝试将string[] keywords
更改为HashSet<string> keywords
。您对“Contains”的调用本质上是一个循环,它比使用散列键查找要慢得多。
如果你想获得真正的花哨,可以使用一些PLINQ来使用多个线程,但我会确保你在走这条路线之前优化了你的单线程性能。