给定两个不同的 BLAS 实现,我们可以期望它们进行完全相同的浮点计算并返回相同的结果吗?或者它是否会发生,例如,一个计算标量积为 和一个计算为 因此可能在 IEEE 浮点数中给出不同的结果算术?
BLAS 实现是否保证给出完全相同的结果?
不,这不能保证。如果您使用的是没有任何优化的 NETLIB BLAS,那么结果几乎是相同的。但是对于 BLAS 和 LAPACK 的任何实际使用,使用高度优化的并行 BLAS。并行化导致,即使它仅在 CPU 的向量寄存器内并行工作,单个项的评估顺序也会发生变化,并且求和的顺序也会发生变化。现在从 IEEE 标准中缺少的关联属性得出结果是不一样的。所以你提到的事情可能会发生。
在 NETLIB BLAS 中,标量积只是一个被因子 5 展开的 for 循环:
DO I = MP1,N,5
DTEMP = DTEMP + DX(I)*DY(I) + DX(I+1)*DY(I+1) +
$ DX(I+2)*DY(I+2) + DX(I+3)*DY(I+3) + DX(I+4)*DY(I+4)
END DO
如果每个乘法立即添加到 DTEMP,或者是否首先将所有 5 个分量相加然后添加到 DTEMP,则取决于编译器。在 OpenBLAS 中,它依赖于架构一个更复杂的内核:
__asm__ __volatile__
(
"vxorpd %%ymm4, %%ymm4, %%ymm4 \n\t"
"vxorpd %%ymm5, %%ymm5, %%ymm5 \n\t"
"vxorpd %%ymm6, %%ymm6, %%ymm6 \n\t"
"vxorpd %%ymm7, %%ymm7, %%ymm7 \n\t"
".align 16 \n\t"
"1: \n\t"
"vmovups (%2,%0,8), %%ymm12 \n\t" // 2 * x
"vmovups 32(%2,%0,8), %%ymm13 \n\t" // 2 * x
"vmovups 64(%2,%0,8), %%ymm14 \n\t" // 2 * x
"vmovups 96(%2,%0,8), %%ymm15 \n\t" // 2 * x
"vmulpd (%3,%0,8), %%ymm12, %%ymm12 \n\t" // 2 * y
"vmulpd 32(%3,%0,8), %%ymm13, %%ymm13 \n\t" // 2 * y
"vmulpd 64(%3,%0,8), %%ymm14, %%ymm14 \n\t" // 2 * y
"vmulpd 96(%3,%0,8), %%ymm15, %%ymm15 \n\t" // 2 * y
"vaddpd %%ymm4 , %%ymm12, %%ymm4 \n\t" // 2 * y
"vaddpd %%ymm5 , %%ymm13, %%ymm5 \n\t" // 2 * y
"vaddpd %%ymm6 , %%ymm14, %%ymm6 \n\t" // 2 * y
"vaddpd %%ymm7 , %%ymm15, %%ymm7 \n\t" // 2 * y
"addq $16 , %0 \n\t"
"subq $16 , %1 \n\t"
"jnz 1b \n\t"
...
它将标量积拆分为长度为 4 的小标量积并将它们相加。
使用其他典型的 BLAS 实现,如 ATLAS、MKL、ESSL,......这个问题保持不变,因为每个 BLAS 实现都使用不同的优化来获得快速代码。但据我所知,需要一个人为的例子来导致真正错误的结果。
如果需要 BLAS 库返回相同的结果(按位相同),则必须使用可重现的 BLAS 库,例如:
- ReproBLAS http://bebop.cs.berkeley.edu/reproblas/
- exBLAS https://exblas.lip6.fr/
简短的回答
如果编写两个 BLAS 实现以完全相同的顺序执行操作,并且库是使用相同的编译器标志和相同的编译器编译的,那么它们会给你相同的结果。浮点运算不是随机的,因此两个相同的实现将给出相同的结果。
但是,为了性能,有很多事情可以打破这种行为......
更长的答案
IEEE 还指定了执行这些操作的顺序,以及每个操作的行为方式。但是,如果您使用“-ffast-math”等选项编译 BLAS 实现,编译器可以执行在精确算术中为真但在 IEEE 浮点中“不正确”的转换。正如您所指出的,典型的例子是浮点加法的非关联性。使用更积极的优化设置,将假定关联性,并且处理器将通过重新排序操作尽可能多地并行执行。
另一个打破标准的行为是通过使用 FMA(融合乘加)指令来实现的。这些在矩阵乘法等运算中很突出,它们有可能使您的例程的吞吐量翻倍。但是,它们在单个操作中操作,并且只产生一个浮点舍入步骤。这偏离了 IEEE 标准,该标准要求此操作具有两个舍入步骤。这使得 FMA 结果实际上比 IEEE 的结果更准确,但它在技术上是违反标准的行为。