并行性:完全不同的浮点结果?

dsi*_*cha 9 floating-point parallel-processing numerical d

我正在尝试为D编程语言调试我的并行库.最近提交了一个错误报告,指出使用任务执行的某些浮点运算的低位比特在运行期间是不确定的.(如果您阅读报告,请注意并行缩减通过以确定的方式创建任务而在幕后工作.)

这似乎不是舍入模式问题,因为我尝试手动设置舍入模式.我也很确定这不是一个并发错误.该库是经过严格测试(包括传递金克斯压力测试),这个问题始终限制在低位,这甚至发生在单核的机器,其中低级别的内存模型问题是一个问题较少.浮点结果可能因调度操作的线程而异的原因还有哪些?

编辑:我在这里做一些printf调试,看起来各个任务的结果有时会在运行中有所不同.

编辑#2:以下代码以更简单的方式再现此问题.它总结了主线程中数组的术语,然后启动一个新线程来执行完全相同的函数.问题绝对不是我的库中的错误,因为这段代码甚至不使用我的库.

import std.algorithm, core.thread, std.stdio, core.stdc.fenv;

real sumRange(const(real)[] range) {
    writeln("Rounding mode:  ", fegetround);  // 0 from both threads.
    return reduce!"a + b"(range);
}

void main() {
    immutable n = 1_000_000;
    immutable delta = 1.0 / n;

    auto terms = new real[1_000_000];
    foreach(i, ref term; terms) {
        immutable x = ( i - 0.5 ) * delta;
        term = delta / ( 1.0 + x * x ) * 1;
    }

    immutable res1 = sumRange(terms);
    writefln("%.19f", res1);

    real res2;
    auto t = new Thread( { res2 = sumRange(terms); } );
    t.start();
    t.join();
    writefln("%.19f", res2);
}
Run Code Online (Sandbox Code Playgroud)

输出:

舍入模式:0

0.7853986633972191094

舍入模式:0

0.7853986633972437348

另一个编辑

这是我用十六进制打印时的输出:

舍入模式:0

0x1.921fc60b39f1331cp-1

舍入模式:0

0x1.921fc60b39ff1p-1

此外,这似乎只发生在Windows上.当我在Linux VM上运行此代码时,我得到两个线程的相同答案.

答案:事实证明,根本原因是浮点状态在主线程上的初始化方式与在D上的Windows上的其他线程不同.请参阅我刚刚提交的错误报告.

Mic*_*rdt 2

本文解释了相同的 C 代码可能导致略有不同的结果的多种原因。在您的情况下,最可能的原因是 CPU 内部指令重新排序。

期望浮点计算对于低位具有确定性是完全错误的。这并不是浮点数的设计目的。

  • 快速浏览了一下之后,我没有看到它在哪里谈到了 CPU 级指令重新排序。另外,据我了解,如果任何对 CPU 重新排序的指令确实导致了哪怕一点点的差异,那么它应该被视为 CPU 错误。-- OTOH 提到了编译器级别的同一类事情(代码中的微小变化导致不同的舍入位置等),这是一个真正的问题。(顺便说一句,在 D/x86 中,“real”是一个 80 位浮点数,因此数学永远不应该截断。) (3认同)