如何解决数值非关联性以进行并行约简?


17

并行归约假设相应的操作是关联的。添加浮点数违反了该假设。您可能会问为什么我对此很在意。好吧,它使结果的再现性较差。当使用模拟退火来优化(或拟合参数)子程序时,情况变得更糟,从而产生不可再现的结果。

解决此问题的常用方法是什么?关于以下策略可以说些什么?

  • 不在乎不可复制性。
  • 不要对浮点数和加法运算使用并行约简。
  • 以可复制的方式创建适当大小的工作包,并手动进行最终缩减。
  • 使用更高的精度进行添加(但并非所有编译器都提供更高精度的浮点类型)。

您是否担心相同数量的过程的可重复性或不同数量的处理器的可重复性?您愿意为按位重现性付出多少性能提升?您只对模拟退火感兴趣吗?
杰德·布朗

@JedBrown我担心获得可再现结果的可能性,例如为了调试潜在问题。如果有一种方法可以重现结果,例如通过使用相同数量的处理器(或仅“知道”最初使用的处理器数量),对我来说很好。我愿意承受与对添加本身使用更高精度的浮点类型相关的性能损失。我的具体问题主要与模拟退火和意想不到的差异有关,但事实证明这些都是真正的错误。
Thomas

Answers:


15

MPI_Allreduce()只要您使用相同数量的处理器,使用实现的还原操作就可以重现,前提是该实现遵循MPI-2.2标准第5.9.1节中出现的以下说明。

给实施者的建议。强烈建议MPI_REDUCE实施此方法,以便每当函数以相同的顺序出现在相同的参数上时,都将获得相同的结果。请注意,这可能会阻止利用处理器的物理位置进行优化。(对实施者的建议结束。)

如果您需要不计成本地保证可重复性,则可以遵循下一段中的准则:

给用户的建议。某些应用程序可能无法忽略浮点操作的非关联性质,或者可能使用需要特殊归约顺序且不能视为关联的用户定义操作(请参阅第5.9.5节)。此类应用程序应明确执行评估顺序。例如,在需要严格的从左到右(或从右到左)评估顺序的操作的情况下,可以通过在单个过程中(例如使用MPI_GATHER)收集所有操作数,并 应用归约操作来完成此操作。以所需的顺序(例如,以 MPI_REDUCE_LOCAL),并在需要时将结果广播或分散到其他进程(例如,以MPI_BCAST)。(对用户的建议结束。)

在更广泛的方案中,大多数应用程序的有效算法都利用了局部性。由于在不同数量的进程上运行时该算法实际上是不同的,因此在不同数量的进程上运行时精确地重现结果只是不切实际。带有阻尼雅可比(Jacobi)或多项式(例如Chebyshev)平滑器的多重网格可能是个例外,这种简单方法可能表现得很好。

使用相同数量的进程,按接收消息的顺序(例如使用MPI_Waitany())处理消息通常会提高性能,这会带来不确定性。在这种情况下,您可以实现两个变体,快速变体以任何顺序接收,而“调试”变体以静态顺序接收。这要求还编写所有基础库来提供此行为。

对于某些情况下的调试,您可以隔离不提供此可重现行为的部分计算,并冗余地执行。根据组件的设计方式,该更改可能是少量的代码或非常麻烦的代码。


6

在很大程度上,我同杰德的答案一样。但是,有另一种解决方法:给定正常浮点数的大小,您可以将每个数字存储在4000左右的定点数中。因此,如果您减少嵌入的浮点数,则无论关联性如何,都将获得精确的计算。(对不起,我没有提到谁提出了这个想法。)


1
我不认为他是第一个,但是您的带宽博士无疑在这个主题上写得
Jeff

5

您可以像在串行中一样在MPI中实现数值稳定的归约算法。当然,可能会影响性能。如果您有能力复制该载体,则只需使用MPI_Gather并在根上进行序列上的数值稳定减少。在某些情况下,您可能会发现对性能的影响并不大。

另一个解决方案是使用此处所述的宽累加器。您可以使用MPI进行此操作,以减少用户定义,尽管它将占用更多带宽。

上面的折衷方案是使用补偿求和。有关详细信息,请参见参考文献“ Kahan总和”。Higham的“ 数值算法的准确性和稳定性 ”是有关此主题的出色资源。



2

我想指出的是,有可能使用补偿求和而不是使用高精度算术(见[1])。这可以提高求和的准确性,而无需求助于更大的数据类型。

[1] Higham,NJ浮点求和的精度。SIAM科学计算杂志14,783–799(1993)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.