我编写了一个非常简单的“字数统计”程序,它读取文件并计算文件中每个单词的出现次数。这是代码的一部分:
class Alaki
{
private static List<string> input = new List<string>();
private static void exec(int threadcount)
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = threadcount;
Parallel.ForEach(Partitioner.Create(0, input.Count),options, (range) =>
{
var dic = new Dictionary<string, List<int>>();
for (int i = range.Item1; i < range.Item2; i++)
{
//make some delay!
//for (int x = 0; x < 400000; x++) ;
var tokens = input[i].Split();
foreach (var token in tokens)
{
if (!dic.ContainsKey(token))
dic[token] = new List<int>();
dic[token].Add(1);
}
}
});
}
public static void Main(String[] args)
{
StreamReader reader=new StreamReader((@"c:\txt-set\agg.txt"));
while(true)
{
var line=reader.ReadLine();
if(line==null)
break;
input.Add(line);
}
DateTime t0 = DateTime.Now;
exec(Environment.ProcessorCount);
Console.WriteLine("Parallel: " + (DateTime.Now - t0));
t0 = DateTime.Now;
exec(1);
Console.WriteLine("Serial: " + (DateTime.Now - t0));
}
}
它简单明了。我使用字典来计算每个单词的出现次数。风格大致基于MapReduce编程模型。如您所见,每个任务都使用自己的私有字典。所以,没有共享变量;只是一堆自己计算单词的任务。以下是代码在四核 i7 CPU 上运行时的输出:
并行:00:00:01.6220927
串行:00:00:02.0471171
加速比约为 1.25,这意味着悲剧!但是当我在处理每一行时添加一些延迟时,我可以达到大约 4 的加速值。
在没有延迟的原始并行执行中,CPU 的利用率几乎没有达到 30%,因此加速不乐观。但是,当我们增加一些延迟时,CPU 的利用率达到了 97%。
首先,我认为原因是程序的 IO 绑定性质(但我认为插入字典在某种程度上是 CPU 密集型的),这似乎是合乎逻辑的,因为所有线程都从共享内存总线读取数据。然而,令人惊讶的是,当我同时运行 4 个串行程序实例(没有延迟)时,CPU 的利用率达到了大约提高,所有四个实例都在大约 2.3 秒内完成!
这意味着当代码在多处理配置中运行时,它达到了大约 3.5 的加速值,但是当它在多线程配置中运行时,加速值约为 1.25。
你有什么想法?我的代码有什么问题吗?因为我认为根本没有共享数据,而且我认为代码不会遇到任何争用。.NET 的运行时是否存在缺陷?
提前致谢。