在 ARMv8 环境中使用向量寄存器 (NEON) 的集合时,双精度不适用
Posted
技术标签:
【中文标题】在 ARMv8 环境中使用向量寄存器 (NEON) 的集合时,双精度不适用【英文标题】:When using an assembly of vector registers(NEON) in ARMv8 environment, double-precision does not apply 【发布时间】:2021-09-27 10:05:49 【问题描述】:我在 ARMv8 环境中使用双精度数据类型进行编码。当给定优化选项-O3时,用C语言实现的值与在Assembly(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
这里是 C 文件
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 -O0 选项,这两个函数的结果是完全一样的。但是执行gcc-O3时,两个函数的结果值并不完全相同,只有12位十进制与单精度相同。我们想知道其中的原因。
我们的比较功能很简单。 if( ((double) a[i].v != (double)b[i].v)) printf("Error\n")
【问题讨论】:
您能否添加有关该问题的更多详细信息?例如,您能否展示问题中提到的 C 代码行、汇编行和值以及使用的编译器? 使用FMA指令之类的声音。检查编译器供应商提供的文档。可能有一个选项可以强制对所有子表达式进行正确舍入。 我附上了代码和其他解释。我们也使用 Jetson Xavier (ARMv8.2),我们使用 gcc 7.5.0。我们确认 ARMv8 浮点指令集支持双精度。 C 标准不要求 C 运算符和 IEEE-754 浮点算术运算之间直接对应,也不要求在计算浮点表达式时使用标称精度。在 C 中,a*b + c
可以编译为 a
和 b
的乘法(四舍五入到类型中可表示的最接近的值),然后是 c
的加法(也四舍五入)或融合乘法- add(计算单个结果 a•b+c 就好像最后只有一个舍入)。此外,float
表达式可以使用double
类型等计算。
因此,如果您出于某种原因想要将您的程序集与 C 代码相匹配,您可以编译 C 代码并向编译器发出请求以显示生成的程序集(在 GCC 和 Clang 中为-S
) 然后重写您的程序集以匹配。但通常没有理由这样做。您要解决的实际问题是什么?您真的需要程序集与 C 完全匹配吗?为什么?还是您担心不匹配表明计算中存在一些错误?
【参考方案1】:
您可以从the compiler output 看到,GCC 发出fmla
指令来同时进行乘法和加法。这比单独执行fmul/fadd
更快更准确。因此,如果 C 函数和您的汇编函数之间的输出存在差异,人们会认为是您的汇编函数给出了更糟糕的答案。您应该能够确认这一点,例如,通过执行以四精度(ARM64 上的long double
)或任意精度数学包计算结果的测试。
我不知道你为什么要故意让 C 函数的行为变得更糟,但如果你这样做了,你可以使用选项 -ffp-contract=off
来禁用融合乘加。 See on godbolt.
附带说明,如果您希望编译器生成像您这样的矢量化代码,您至少需要将指针参数 c
声明为 restrict
(当然要确保 c
数组永远不会重叠a
或b
)。否则 GCC 将测试别名并在需要时回退到非矢量化版本,see on godbolt。
您可能还想升级您的编译器。上面的链接是 GCC 11.1 的,GCC 7.3 生成的代码是still vectorized but much more complicated。
【讨论】:
以上是关于在 ARMv8 环境中使用向量寄存器 (NEON) 的集合时,双精度不适用的主要内容,如果未能解决你的问题,请参考以下文章
Armv8a NEON 内联汇编代码:如何将 16x8 位向量转换为四个 4x32 位(整数)向量?
使用 NEON 在 ARM 汇编中对四字向量中的所有元素求和