.NET 4:并行求和不爽

并行这个概念近来很火,.NET 4 中也引入了 TPL(任务并行库) 和 PLinq(并行Linq)来简化并行编程。
于是想体验下,便从最简单的并行求和开始,看看并行的效率。
我用的 CPU 是 i3 530 处理器,属于伪四核。想来如果四核并行求和的话,耗时应是非并行的 25% 左右。

并行求和效率测试

于是编码进行验证,先生成一个满是随机数的数组:
1 2 3 4
var random = new Random(); var data = Enumerable.Range(1, 67108864) .Select(i => (long)random.Next(int.MaxValue)) .ToArray();
这个数组比较大,生成大约要 5 秒钟时间。
测试时使用 Stopwatch 类计时:
1 2 3 4 5 6 7 8 9 10 11 12
Stopwatch w = new Stopwatch(); //并行 w.Start(); var sum1 = data.AsParallel().Sum(); w.Stop(); Console.WriteLine(w.ElapsedMilliseconds); //非并行 w.Reset(); w.Start(); var sum2 = data.Sum(); w.Stop(); Console.WriteLine(w.ElapsedMilliseconds);
执行多次结果如下(考虑到执行顺序或许会对结果产生影响,后面五次的执行是把非并行求和放在并行求和前面进行测试的):
  1 2 3 4 5 6 7 8 9 10
并行 385 385 376 409 437 342 419 347 379 342
非并行 733 666 668 665 669 673 667 670 669 672
(单位:毫秒)
平均起来并行求和只用了 57% 的时间,相对于非并行。虽然效率有提高,但离我们最初设想的 25% 差多了。
莫非只用了两个核,不如从任务管理器中查看下 CPU 使用情况。但是单次执行时间不到一秒,时间太短,太易看清,在代码中再加上个循环:
1 2 3 4
for (int i = 0; i < 100; i++) { var sum = data.AsParallel().Sum(); }
从下图中可以看出,四核已全部用上:
image.NET 4:并行求和不爽
看来应该是并行求和算法的问题了。不如自己改进下。

并行求和算法

思路很简单,把数组分成多块,每个 CPU 核心负责一块的求和,最后把每块的和加起来就是了:
1 2 3 4 5
var partitionCount = 4; //分块数量 var partitionLength = (data.Count() - 1) / partitionCount + 1; //每块长度 var results = new long[partitionCount]; //声明一个数组,用来保留更块的和 var r = Parallel.For(0, partitionCount, i => results[i] = PartitionSum(data, partitionLength * i, partitionLength)); var sum3 = results.Sum(); //将各块的和加起来
PartitionSum 是一个函数,用来计算某一块的和:
1 2 3 4 5 6 7 8 9
public static long PartitionSum(long[] source, int start, int length) { long result = 0; int end = start + length; if (end > source.Length) end = length; for (int i = start; i < end; i++) result += source[i]; return result; }
测试时间为:
  1 2 3 4 5 6 7 8 9 10
并行改进 153 161 151 154 152 156 157 153 155 153
占非行执行时间的 23%,与我们最初的预计差不多,要好些。

总结

本文的测试虽然比较简单,粗陋,也不权威,但也从一定程度上说明了 .NET 4 的并行求和算法效率不高
本文仅对 .NET 4 的并行求和进行了测试,只涉及到 .NET 4 中并行的很少一部分。.NET 4 的并行求和算法效率不高并不表示其它部分效率也不高。
Tags: 

延伸阅读

最新评论

发表评论