OpenCL浮点精度

Tap*_*ped 5 c++ windows precision opencl

我在OpenCL中发现了主机 - 客户端浮点标准的问题.问题是,在x86中编译时,Opencl计算的浮点数与我的visual studio 2010编译器的浮点数不同.但是,在x64中进行编译时,它们处于相同的限制.我知道它必须与之相关,http://www.viva64.com/en/b/0074/

我在测试过程中使用的来源是:http://www.codeproject.com/Articles/110685/Part-1-OpenCL-Portable-Parallelism 当我在x86中运行程序时,它会给我202个相同的数字,当时内核和C++程序占用了1269760个数字.然而,在64位构建中,1269760数字是正确的,换句话说是100%.此外,我发现opencl和x86 c ++的计算结果之间的误差是5.5385384e-014,这是一个非常小的部分但不够小,与数字的epsilon相比,即2.92212543378266922312416e-19.
这是因为,错误需要小于epsilon,因此程序可以将这两个数字识别为一个相同的数字.当然,通常人们永远不会比较本地的浮子,但很高兴知道浮动限制是不同的.是的,我试图设置flt:static,但得到了同样的错误.

所以我想对这种行为做一些解释.提前感谢所有答案.

Hri*_*iev 9

由于在将项目从x86切换到x64时GPU代码没有任何变化,所以这一切都必须像在CPU上执行乘法一样.在x86和x64模式下浮点数处理之间存在一些细微差别,最大的一点是,由于任何x64 CPU也支持SSE和SSE2,因此默认情况下它用于Windows上64位模式下的数学运算.

HD4770 GPU使用单精度浮点单元完成所有计算.另一方面,现代x64 CPU有两种处理浮点数的功能单元:

  • x87 FPU,具有更高的80位扩展精度
  • SSE FPU以32位和64位精度运行,与其他CPU处理浮点数的方式非常兼容

在32位模式下,编译器不会假设SSE可用并生成通常的x87 FPU代码来进行数学运算.在这种情况下,类似data[i] * data[i]的操作是使用更高的80位精度在内部执行的.该类型的比较if (results[i] == data[i] * data[i])如下进行:

  • data[i] 使用.将其推入x87 FPU堆栈 FLD DWORD PTR data[i]
  • data[i] * data[i] 使用计算 FMUL DWORD PTR data[i]
  • result[i] 使用将其推入x87 FPU堆栈 FLD DWORD PTR result[i]
  • 使用两个值进行比较 FUCOMPP

这就是问题所在.data[i] * data[i]以80位精度驻留在x87 FPU堆栈元素中.result[i]来自GPU的32位精度.这两个数字很可能会有所不同,因为data[i] * data[i]有更多的有效数字,而result[i]有很多零(80位精度)!

在64位模式下,事情以另一种方式发生.编译器知道您的CPU支持SSE,它使用SSE指令进行数学运算.在x64上以下列方式执行相同的比较语句:

  • data[i] 使用加载到SSE寄存器中 MOVSS XMM0, DWORD PTR data[i]
  • data[i] * data[i] 使用计算 MULSS XMM0, DWORD PTR data[i]
  • result[i] 使用加载到另一个SSE寄存器 MOVSS XMM1, DWORD PTR result[i]
  • 使用两个值进行比较 UCOMISS XMM1, XMM0

在这种情况下,使用与GPU上使用的相同的32位单点精度执行平方运算.不会生成80位精度的中间结果.这就是为什么结果是一样的.

即使没有涉及GPU,也很容易实际测试.只需运行以下简单程序:

#include <stdlib.h>
#include <stdio.h>

float mysqr(float f)
{
    f *= f;
    return f;
}

int main (void)
{
    int i, n;
    float f, f2;

    srand(1);
    for (i = n = 0; n < 1000000; n++)
    {
        f = rand()/(float)RAND_MAX;
        if (mysqr(f) != f*f) i++;
    }
    printf("%d of %d squares differ\n", i);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

mysqr是专门编写的,以便中间80位结果将以32位精度进行转换float.如果您在64位模式下编译并运行,则输出为:

0 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)

如果编译并以32位模式运行,则输出为:

999845 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)

原则上你应该能够在32位模式下更改浮点模型(项目属性 - >配置属性 - > C/C++ - >代码生成 - >浮点模型),但这样做至少在VS2010中间没有任何改变结果仍保留在FPU中.您可以做的是强制存储和重新加载计算的方块,以便在将其与GPU的结果进行比较之前将其舍入为32位精度.在上面的简单示例中,这是通过更改:

if (mysqr(f) != f*f) i++;
Run Code Online (Sandbox Code Playgroud)

if (mysqr(f) != (float)(f*f)) i++;
Run Code Online (Sandbox Code Playgroud)

更改后,32位代码输出变为:

0 of 1000000 squares differ
Run Code Online (Sandbox Code Playgroud)