当在ARMv8环境中使用向量寄存器汇编(NEON)时,双精度不适用



我在ARMv8环境中使用双数据类型编码。当给出优化选项-O3时,在C语言中实现的值与在汇编语言(NEON)中使用ARMv8指令实现的值不同。单独使用FMUL和FADD时,结果值相同,但同时使用FMUL + FADD时,结果值与C语言不同。我们要解决这个问题。

这里的程序集文件

.data
.text
.global Asm_Operation_Test
Asm_Operation_Test:
MOV x3,#0
Operation_Loop:
LD1 {v0.2d-v3.2d},[x0],#64
LD1 {v4.2d-v7.2d},[x1],#64
FMUL v0.2d,v0.2d,v4.2d
FADD v0.2d,v0.2d,v4.2d
FMUL v1.2d,v1.2d,v5.2d
FADD v1.2d,v1.2d,v5.2d
FMUL v2.2d,v2.2d,v6.2d
FADD v2.2d,v2.2d,v6.2d
FMUL v3.2d,v3.2d,v7.2d
FADD v3.2d,v3.2d,v7.2d
ST1 {v0.2d-v3.2d},[x2],#64
ADD x3,x3,#1
CMP x3,#32
BNE Operation_Loop
ret

here C file

typedef struct {double v;} fpr;
C_Operation Test(fpr*a, fpr*b, fpr*c){
for(int i=0; i<256; i++)
{
c[i].v = a[i].v * b[i].v + b[i].v;
}
}
汇编函数和C函数彼此执行相同的操作。输入数据为double类型,输入256个随机数(double)的数组。如果添加了gcc - 0选项,则两个函数的结果完全相同。但是,在执行gc - o3时,两个函数的结果值并不完全相同,只有12个十进制与单精度相同。我们想知道原因。

我们的比较函数很简单。If ((double) a[i]。v ! =(双)b[我].v)) printf("错误 n"

您可以从编译器输出中看到,GCC发出fmla指令来同时进行乘法和加法运算。这比单独做fmul/fadd更快更准确。所以在某种程度上,C函数和你的集合函数的输出是不一致的,你会认为是你的集合函数给出了更差的答案。您应该能够确认这一点,例如,通过执行以四精度(ARM64上的long double)计算结果的测试或使用任意精度的数学包。

我不确定为什么您要故意使C函数表现得更差,但是如果您这样做,您可以使用选项-ffp-contract=off来禁用融合乘法-加的使用。

作为附带说明,如果您希望编译器生成像您这样的矢量化代码,则需要至少将指针参数c声明为restrict(当然要确保c数组永远不会与ab重叠)。否则,GCC将测试混叠,并在需要时退回到非矢量化版本,参见godbolt。

你可能还想升级你的编译器。上面的链接是使用GCC 11.1的,GCC 7.3生成的代码仍然是矢量化的,但要复杂得多。

最新更新