寻求有关计算文件中密码唯一出现次数的各种方法之间性能差异的解释



我试图用C#计算每个唯一密码在rockyou.txt文件中出现的次数,但由于我对算法很弱,我决定在Release配置中实现我能想到的每一种暴力方法(总共五种)和时间。
我对结果感到非常惊讶,并寻求解释为什么每种方法都比以前更快:

问题更新如下:

为了后验性,我发布了我的最新结果。

设置:

ConcurrentDictionary<string, int> conDict = new();
Dictionary<string, int> dict = new();
string[] lines = File.ReadAllLines(@"rockyou.txt");
IEnumerable<string> linesEnum = File.ReadLines(@"rockyou.txt");

第一种方法:
这里的想法是迭代每个密码(每一行),如果它存在于字典中,则增加它出现的次数。如果它不存在,它将触发一个异常,try-catch将确保密码被添加到字典中。

foreach (var line in lines)
{
try
{
dict[line] += 1;
}
catch
{
dict[line] = 1;
}
}

第二种方法:

foreach (var line in lines)
{
if (dict.ContainsKey(line))
dict[line] += 1;
else
dict[line] = 1;
}

第三种方法:

foreach (var line in lines)
conDict.AddOrUpdate(line, 1, (id, count) => count + 1);

第四种方法:

Parallel.ForEach(lines, line => conDict.AddOrUpdate(line, 1, (id, count) => count + 1));

第五种方法:

var res = lines.GroupBy(line => line).ToDictionary(group => group.Key, group => group.Count());

第六种方法:

var wordCounts = from w in lines
group w by w into g
select new { Word = g.Key, Count = g.Count() };
var result = wordCounts.ToList();

第七种方法:

var dict = linesEnum.GroupBy(line => line).ToDictionary(group => group.Key, group => group.Count());

第八种方法:

foreach (var line in lines)
dict[line] = (dict.TryGetValue(line, out var count) ? count : 0) + 1;

基准人员简历

>
方法平均值错误StdDev right;">Gen 2分配
一个2031.4毫秒384 B
两个2176.9毫秒96 B
三个351.7毫秒384 B
四个548.0毫秒23712 B
五个19952.9毫秒2863095616 B
六个118398.0毫秒22258200168 B
Seven25000.0 ms3506923376 B
八个2059.4毫秒96 B

算法是一样的,到处都使用哈希表。到你的方法

  1. 不要使用异常来定义程序的标准流。他们在这方面异常缓慢
  2. 有点次优,应该使用linesEnum
  3. 您使用的是并发收集,而不需要并发。这会给你带来性能上的冲击
  4. 好的,如果你需要最大的性能
  5. 。。7.林克会给你一些开销,这项任务太琐碎了
  6. 好,但使用lineEnum

通常,如果您可以将文件作为管道处理,请执行此操作。它可以节省内存,也有助于提高速度。您还希望最大限度地减少对字典的访问次数,TryGetValue通常就是您所需要的。从简单代码开始

foreach (var line in File.ReadLines(@"rockyou.txt"))
{
dict.TryGetValue(line, out var count);
dict[line] = count + 1;               
}

最有可能的瓶颈是从文件中读取。如果是这样的话,你就完了。您可以使用并行处理,但除非从光盘读取的速度快于处理速度,否则不要指望有显著的改进。

最新更新