我在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
数组永远不会与a
或b
重叠)。否则,GCC将测试混叠,并在需要时退回到非矢量化版本,参见godbolt。
你可能还想升级你的编译器。上面的链接是使用GCC 11.1的,GCC 7.3生成的代码仍然是矢量化的,但要复杂得多。