繁体   English   中英

两个单精度浮点向量的点积在CUDA内核中产生的结果与在主机上的结果不同

[英]Dot product of two single-precision floating point vectors yields different results in CUDA kernel than on the host

在调试一些CUDA代码时,我正在使用printf语句与等效的CPU代码进行比较,并注意到在某些情况下我的结果不同; 它们在任何一个平台上都不一定是错的,因为它们处于浮点舍入误差范围内,但我仍然有兴趣知道是什么导致了这种差异。

我能够将问题跟踪到不同的点积结果。 在CUDA和主机代码中,我都有float4类型的向量a和b。 然后,在每个平台上,我使用以下代码计算点积并打印结果:

printf("a: %.24f\t%.24f\t%.24f\t%.24f\n",a.x,a.y,a.z,a.w);
printf("b: %.24f\t%.24f\t%.24f\t%.24f\n",b.x,b.y,b.z,b.w);
float dot_product = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
printf("a dot b: %.24f\n",dot_product);

并由此产生的CPU打印输出:

a: 0.999629139900207519531250   -0.024383276700973510742188 -0.012127066962420940399170 0.013238593004643917083740
b: -0.001840781536884605884552  0.033134069293737411499023  0.988499701023101806640625  1.000000000000000000000000
a dot b: -0.001397025771439075469971

对于CUDA内核:

a: 0.999629139900207519531250   -0.024383276700973510742188 -0.012127066962420940399170 0.013238593004643917083740
b: -0.001840781536884605884552  0.033134069293737411499023  0.988499701023101806640625  1.000000000000000000000000
a dot b: -0.001397024840116500854492

正如您所看到的,a和b的值似乎在两个平台上都是按位等价的,但完全相同的代码的结果略有不同。 据我所知,浮点乘法是根据IEEE 754标准明确定义的,并且与硬件无关。 但是,我确实有两个假设,为什么我没有看到相同的结果:

  1. 编译器优化是对乘法进行重新排序,它们在GPU / CPU上以不同的顺序发生,从而产生不同的结果。
  2. CUDA内核使用融合的multipl-add(FMA)运算符,如http://developer.download.nvidia.com/assets/cuda/files/NVIDIA-CUDA-Floating-Point.pdf中所述 在这种情况下,CUDA结果实际上应该更准确一些。

除了将FMUL和FADD合并到FMA中(可以使用nvcc命令行开关-fmad=false关闭),CUDA编译器会遵循C / C ++规定的评估顺序。 根据CPU代码的编译方式,它可能会使用比单精度更高的精度来累积点积,从而产生不同的结果。

对于GPU代码,将FMUL / FADD合并到FMA中是常见的,因此产生的数值差异也是如此。 由于性能原因,CUDA编译器执行积极的FMA合并。 使用FMA通常也会产生更准确的结果,因为减少了舍入步骤的数量,并且由于FMA在内部维护全宽度产品,因此存在一些防减法消除的保护。 我建议阅读以下白皮书,以及它引用的参考文献:

https://developer.nvidia.com/sites/default/files/akamai/cuda/files/NVIDIA-CUDA-Floating-Point.pdf

要使CPU和GPU结果与完整性检查匹配,您可能希望使用-fmad=false关闭GPU代码中的FMA合并,并在CPU上强制执行每个中间结果以单精度存储:

   volatile float p0,p1,p2,p3,dot_product; 
   p0=a.x*b.x; 
   p1=a.y*b.y; 
   p2=a.z*b.z; 
   p3=a.w*b.w; 
   dot_product=p0; 
   dot_product+=p1; 
   dot_product+=p2; 
   dot_product+=p3;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM