4

我有一个修改 FPU 控制字的大型 C++ 程序(使用_controlfp())。它取消屏蔽一些 FPU 异常并安装 SEHTranslator 以生成类型化的 C++ 异常。我正在使用 VC++ 9.0。

我想使用 OpenMP (v.2.0) 来并行化我们的一些计算循环。我已经成功地将它应用于一个,但数值结果略有不同(尽管我理解这也可能是由于计算以不同的顺序执行的)。我假设这是因为 FPU 状态是线程特定的。有没有办法让 OpenMP 线程从主线程继承该状态?或者是否有某种方法可以使用 OpenMP 指定新线程执行设置正确状态的特定函数?处理这种情况的惯用方法是什么?

4

3 回答 3

1
  1. 正如您已经指出的那样,双精度/浮点运算在数学中不是关联/交换/分布的实数。特别是,当你改变计算顺序时,乘/除巨大的数字/非常小的数字可能会导致明显的精度错误。

  2. FPU 状态应该是特定于线程的,因为状态表示为寄存器,而寄存器状态(=上下文)特定于线程。

  3. 说派生线程继承主线程的状态是模棱两可的,因为在这种情况下状态是不明确的。如果您的意思是注册状态,则不是。

  4. 我的建议是你为什么不简单地为每个线程设置 FPU 控制字?例如,在生成 OpenMP 线程之前,即在 parallel-for 之前,使用_status87将当前 FPU 控制字存储在全局变量中。然后, put 语句读取全局变量并在并行迭代中设置新值。由于它对全局变量是只读的,因此您不必担心任何数据竞争。

unsigned int saved_status = _status87();
#pragma omp parallel for (...)
for (int i = 0; i < N; ++i)
{
  _controlfp(saved_status, ...);

  ..
}
于 2010-02-09T06:22:26.490 回答
0

这很可能与浮点运算的顺序有关。我们都依赖于我们的操作是关联的和可交换的,但不幸的事实是浮点操作不是可交换的,所以当它们被并行化时,结果可能会有所不同,因为顺序是随机的。

尝试向后运行循环并查看结果是否不同。

如果您确实有每个线程的需求,OMP 提供有关循环迭代落在同一线程上的保证,即如果您在四核上循环从 1 到 N,则迭代 1 到 N/4 将在同一线程上运行。

-瑞克

于 2010-02-09T05:45:38.750 回答
0

我的结论是我没有问题。结果的差异是由于计算的顺序,而不是不同线程中的 FPU 状态(我们没有更改精度或舍入模式)。至于工作线程中的 FPU 异常屏蔽不同,这不是问题,因为如果工作线程执行会导致异常的操作,则该结果(现在为 NaN 或 Inf 等)最终将“考虑”到主线程和异常将被抛出。

此外,必须在引发异常的同一 OpenMP 线程中捕获异常。这意味着我只希望主线程无论如何都能够抛出异常。

于 2010-02-09T16:12:05.873 回答