.NET 4:并行求和不爽
2011-07-13 20:02 鹤冲天 阅读(5930) 评论(30) 编辑 收藏 举报并行这个概念近来很火,.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(); } |
执行,则从下图中可以看出,四核已全部用上:
看来应该是并行求和算法的问题了。不如自己改进下。
简单并行求和算法
思路很简单,把数组分成多块,每个 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 的并行求和算法效率不高并不表示其它部分效率也不高。
后记:真正四核 CPU Xeon E5606 上的测试结果
回复中有朋友提到可能是伪四核 CPU 的原因,于是我找了一台服务器,装有 Xeon E5606,是货真价实的四核处理器。
测试结果如下:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
非并行 | 858 | 863 | 861 | 859 | 858 | 862 | 863 | 859 | 861 | 859 |
并行 | 250 | 248 | 280 | 248 | 248 | 248 | 249 | 248 | 249 | 249 |
并行改进 | 121 | 121 | 121 | 121 | 121 | 121 | 121 | 121 | 121 | 121 |
(单位:毫秒)
相对非并行,.NET 并行用时约 29%,我的简单改进算法约 14%。
29% 与最初预期的 25% 很接近,看来原因就在伪四核上了。
不过另一个问题有也出来了,为什么我那简单的改进算法相对效率那么高。
-------------------
思想火花,照亮世界