修复分布式版本中的算术错误

Fix arithmetic error in distributed version

我正在分布式环境中通过 Cholesky 分解来求逆矩阵,正如所讨论的那样 。我的代码运行良好,但为了测试我的分布式项目是否产生正确的结果,我不得不将它与串行版本进行比较。结果不完全一样!

例如,结果矩阵的最后五个单元格是:

serial gives:
-250207683.634793 -1353198687.861288 2816966067.598196 -144344843844.616425 323890119928.788757
distributed gives:
-250207683.634692 -1353198687.861386 2816966067.598891 -144344843844.617096 323890119928.788757

我在 Intel forum 中有 post 关于那个,但我得到的答案是关于在我将使用分布式版本进行的所有执行中获得相同的结果,这是我已经拥有的.他们似乎(在另一个线程中)无法对此做出回应:

如何在串行和分布式执行之间获得相同的结果?这可能吗? 这将导致修正算术错误。

我试过这样设置:mkl_cbwr_set(MKL_CBWR_AVX); 并使用 mkl_malloc() 来对齐内存,但没有任何改变。我会得到相同的结果,只是在我将为分布式版本生成一个进程(这将使它几乎是串行的)的情况下!

我调用的分布式例程:pdpotrf() and pdpotri().

我调用的串口例程:dpotrf() and dpotri().

你们的分歧好像是在12号左右出现的s.f。由于浮点运算不是真正的关联(即,f-p 运算不能保证 a+(b+c) == (a+b)+c),并且由于并行执行通常不会给出操作应用的确定顺序,所以这些小差异是典型的与串行等效代码相比,并行数字代码的数量。实际上,当 运行 在不同数量的处理器(例如 4 对 8)上时,您可能会观察到相同的差异顺序。

不幸的是,获得确定性结果的简单方法是坚持串行执行。要从并行执行中获得确定性结果,需要付出巨大努力才能非常具体地确定操作的执行顺序,直到最后一个 +*,这几乎可以肯定排除了大多数数字库的使用,并且引导您对大型数字例程进行艰苦的手动编码。

在我遇到的大多数情况下,输入数据的准确性通常来自传感器,无需担心第 12 位或更晚的 s.f。我不知道你的数字代表什么,但对于许多科学家和工程师来说,等于第 4 或第 5 个 sf 对于所有实际目的来说已经足够了。这对数学家来说是另一回事...

正如另一个答案提到的,不能保证在串行和分布式之间获得完全相同的结果。 HPC/distributed 工作负载的一种常见技术是验证解决方案。从计算错误百分比到更复杂的验证方案,有许多技术,例如 used by the HPL。这是一个计算百分比误差的简单 C++ 函数。正如@HighPerformanceMark 在他的 post 中指出的那样,对这种数值错误的分析非常复杂;这是一个非常简单的方法,网上有很多关于该主题的信息。

#include <iostream>
#include <cmath>

double calc_error(double a,double x)
{
  return std::abs(x-a)/std::abs(a);
}
int main(void)
{
  double sans[]={-250207683.634793,-1353198687.861288,2816966067.598196,-144344843844.616425, 323890119928.788757};
  double pans[]={-250207683.634692, -1353198687.861386, 2816966067.598891, -144344843844.617096, 323890119928.788757};
  double err[5];
  std::cout<<"Serial Answer,Distributed Answer, Error"<<std::endl;
  for (int it=0; it<5; it++) {
    err[it]=calc_error(sans[it], pans[it]);
    std::cout<<sans[it]<<","<<pans[it]<<","<<err[it]<<"\n";
  }
return 0;
}

产生此输出:

Serial Answer,Distributed Answer, Error
-2.50208e+08,-2.50208e+08,4.03665e-13
-1.3532e+09,-1.3532e+09,7.24136e-14
2.81697e+09,2.81697e+09,2.46631e-13
-1.44345e+11,-1.44345e+11,4.65127e-15
3.2389e+11,3.2389e+11,0

如您所见,每种情况下误差的数量级大约为 10^-13 或更小,并且在一种情况下不存在。根据您尝试解决的问题,这个数量级的错误可能被认为是可以接受的。希望这有助于说明一种针对串行解决方案验证分布式解决方案的方法,或者至少提供一种方法来显示并行算法和串行算法相差多远。

在验证大问题和并行算法的答案时,执行多个 运行 并行算法并保存每个 运行 的结果也很有价值。然后,您可以查看结果 and/or 错误是否随并行算法 运行 变化,或者它是否会随着时间的推移而稳定下来。

表明并行算法在超过 1000 运行s 的可接受阈值内产生误差(只是一个例子,数据越多对这类事情越好)对于各种问题大小是评估有效性的一种方法结果。

过去,当我执行基准测试时,我注意到在服务器 "warmed up" 之前的前几个 运行 秒内行为有很大不同。当时我从不费心去检查结果中的错误是否会像性能一样随着时间的推移而稳定下来,但看看它会很有趣。