我试图在一台机器上使用多个线程找到浮点值数组的平均值。我不关心数组的大小或内存约束(假设一个中等大小的数组,大到足以保证多个线程)。特别是,我正在寻找最有效的 调度算法 。在我看来,静态块方法将是最有效的。
因此,鉴于我有x个机器内核,将数组块化为array.size / x值并将每个内核求和各自数组块的结果似乎是合理的。然后,添加每个核心的求和结果,最终结果是该值除以数组元素的总数(注意:如果数组元素的数量不能被x完全整除,我知道优化在线程上尽可能均匀地分布元素。
数组显然会在线程之间共享,但由于没有涉及写入,我不需要涉及任何锁定机制或担心同步问题。
我的问题是:这实际上是解决此问题的最有效方法 吗?
相反,例如,考虑静态交错方法。在这种情况下,如果我有四个核心(线程),那么线程1将对数组元素0,4,8,12进行操作...而线程2将对元素1,5,9,13进行操作...这将看起来更糟糕,因为每个核心都会不断获得缓存未命中,而静态块方法意味着每个核心都运行成功值并利用数据局部性。我运行的一些测试似乎支持了这一点。
那么,任何人都可以指出比静态阻止更好的方法,或者确认这很可能是最好的方法吗?
修改
我正在使用Java和Linux(Ubuntu)。我试图不去考虑所涉及的语言/平台,只是从调度的角度抽象地看待问题,涉及手动将工作负载分配给多个线程。但我明白语言和平台是重要的因素。
修改-2:
这是一些具有不同阵列大小(双倍)的时间(纳秒时间/ 1000)
顺序计时使用单个java线程。
其他人使用并行工作的所有可用(4)核实现了各自的调度策略。
1,000,000个元素:
---连续
5765
1642
1565
1485
1444
1511
1446
1448
1465
1443
---静止块
15857个
4571
1489
1529
1547
1496
1445
1415
1452
1661
---静态交错
9692
4578
3071
7204
5312
2298
4518
2427
1874年
1900年
50,000,000元素:
---连续
73757个
69280个
70255个
78510个
74520个
69001个
69593个
69586个
69399个
69665个
---静止块
62827个
52705个
55393个
53843个
57408个
56276个
56083个
57366个
57081个
57787个
---静态交错
179592个
306106个
239443个
145630个
171871个
303050个
233730个
141827个
162240个
292421
答案 0 :(得分:3)
您的系统似乎没有内存带宽来利用此问题的4个线程。做浮点运算添加元素是不够的工作,以保持CPU忙于速率内存可以提供数据...你的4个内核共享相同的内存控制器/ DRAM ......并在等待内存。如果你使用2个线程而不是4个线程,你可能会看到相同的加速。
交错是一个坏主意,正如您所说并且如您所知,为什么浪费宝贵的内存带宽将数据带入核心,然后仅使用四分之一。如果你很幸运并且线程有点同步,那么你将在第2级或第3级缓存中重用数据,但是你仍然会将数据带入L1缓存并且只使用一小部分。
更新:当添加5000万个元素时,一个问题是精度损失,50万个日志基数2约为26位,双精度浮点数有53个有效位(52个显式和1个隐含) )。最好的情况是所有元素都具有相似的指数(大小相似)。如果数组中的数字具有大范围的指数,情况会变得更糟,在最坏的情况下,范围很大并且它们按照数量级的降序排序。通过对数组进行排序并按升序添加,可以提高最终平均值的精度。请参阅此SO问题,以便在添加大量项目时更深入地了解精度问题:Find the average within variable number of doubles。