3

我试图在单台机器上使用多个线程来查找浮点值数组的平均值。我不关心数组的大小或内存限制(假设一个中等大小的数组,大到足以保证多个线程)。特别是,我正在寻找最有效的调度算法。在我看来,静态块方法将是最有效的。

因此,鉴于我有 x 个机器核心,将数组分块为 array.size/x 值并让每个核心对它们各自的数组块的结果求和似乎是合理的。然后,将每个核心的总和结果相加,最终结果是这个值除以数组元素的总数(注意:在数组元素的 # 不能被 x 整除的情况下,我知道优化在线程中尽可能均匀地分布元素)。

该数组显然将在线程之间共享,但由于不涉及写入,我不需要涉及任何锁定机制或担心同步问题。

我的问题是:这实际上是解决这个问题的最有效方法吗?

相反,例如,考虑静态交错方法。在这种情况下,如果我有四个核心(线程),那么线程一将对数组元素 0、4、8、12 进行操作……而线程二将对元素 1、5、9、13 进行操作……这将看起来更糟,因为每个核心都会不断地获得缓存未命中,而静态块方法意味着每个核心都在成功值上运行并利用数据局部性。我运行的一些测试似乎支持这一点。

那么,谁能指出比静态块更好的方法,或者确认这很可能是最好的方法?

编辑:
我正在使用 Java 和 Linux (Ubuntu)。我尽量不要过多考虑所涉及的语言/平台,而只是从调度的角度抽象地看待问题,涉及手动将工作负载分配给多个线程。但我知道语言和平台是重要因素。

编辑2:
这里有一些时间(纳米时间/ 1000)具有不同的数组大小(双打)。
顺序计时使用单个 java 线程。其他人使用并行工作的所有可用 (4) 内核实现了各自的调度策略。

百万元素:
---连续
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

50000000个元素:
---顺序
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

4

1 回答 1

3

您的系统似乎没有足够的内存带宽来利用 4 个线程来解决这个问题。对元素进行浮点加法不足以使 CPU 保持在内存可以传输数据的速率下忙碌……您的 4 个内核共享相同的内存控制器/DRAM……并且正在等待内存。如果您使用 2 个线程而不是 4 个线程,您可能会看到相同的加速。

正如您所说并确认,交错是一个坏主意,为什么要浪费宝贵的内存带宽将数据带入核心,然后只使用其中的四分之一。如果幸运并且线程在某种程度上同步运行,那么您将在 2 级或 3 级缓存中重用数据,但您仍会将数据带入 L1 缓存并且只使用一小部分。

更新:当添加 5000 万个元素时,一个问题是精度损失,5000 万的 log base 2 约为 26 位,双精度浮点有 53 个有效位(52 个显式和 1 个隐含)。最好的情况是所有元素都具有相似的指数(幅度相似)。如果数组中的数字具有大范围的指数,情况会变得更糟,在最坏的情况下,范围很大并且它们按数量级降序排序。通过对数组进行排序并按升序添加,可以提高最终平均值的精度。请参阅此 SO 问题,以更深入地了解添加大量项目时的精度问题:Find the average within variable number of doubles

于 2013-03-16T20:58:22.257 回答