我正在尝试编写一个函数,它将使用ymm寄存器填充浮点矩阵中的零。
不久之后,我编写了这个函数:
void fillMatrixByZeros(float matrix[N][N]){
for (int k = 0; k < N; k += 8){
for (int i = 0; i < N; ++i){
asm volatile (
"vxorps %%ymm0, %%ymm0, %%ymm0;"
"vmovups %%ymm0, (%0)"
: "=m"(matrix[i] + k)
:
: "%ymm0", "memory"
);
}
}
}
我试着编译我的全部代码,并得到了以下错误:
prog.cpp: In function ‘void fillMatrixByZeros(float (*)[16])’:
prog.cpp:35:8: error: lvalue required in asm statement
35 | );
| ^
prog.cpp:35:8: error: invalid lvalue in asm output 0
我得出了一个结论:matrix[i]+k
是一个rvalue或类似的值,所以不能在那里使用它。
在谷歌搜索之后,我想出了两个解决方案:
First
void fillMatrixByZeros(float matrix[N][N]){
for (int k = 0; k < N; k += 8){
for (int i = 0; i < N; ++i){
asm volatile (
"vxorps %%ymm0, %%ymm0, %%ymm0;"
"vmovups %%ymm0, (%0)"
:
: "r"(matrix[i] + k)
: "%ymm0", "memory"
);
}
}
}
第二
void fillMatrixByZeros(float matrix[N][N]){
long long int matrixPointer;
for (int k = 0; k < N; k += 8){
for (int i = 0; i < N; ++i){
asm volatile (
"vxorps %%ymm0, %%ymm0, %%ymm0;"
"vmovups %%ymm0, (%0)"
: "=r"(matrixPointer)
: "0"(matrix[i] + k)
: "%ymm0", "memory"
);
}
}
}
这些功能正常工作。我想知道为什么。
为什么在第一个函数中没有任何值问题?第二个函数是怎么回事?
发布于 2021-11-18 12:35:47
您不能分配给matrix[i] + k
,所以它不是一个lvalue。m
约束需要内存中的对象,而不是其地址。因此,要解决这个问题,请提供要分配给的对象,而不是它的地址:
void fillMatrixByZeros(float matrix[N][N]){
for (int k = 0; k < N; k += 8){
for (int i = 0; i < N; ++i){
asm volatile (
"vxorps %%ymm0, %%ymm0, %%ymm0;"
"vmovups %%ymm0, %0"
: "=m"(matrix[i][k])
:
: "%ymm0", "memory"
);
}
}
}
这是在内联程序集语句中访问内存中的对象的正确方法。
使用带有操作数地址的r
约束的解决方案,然后也执行显式取消引用工作。但它们可能效率较低,因为它们阻止编译器使用其他寻址模式,如SIB寻址模式。相反,它必须首先在登记簿上显示地址。
你的最后一个例子有点傻。它使用耦合的asm操作数在传递给内联程序集语句之前本质上执行matrixPointer = matrix[i] + k
。这是一种非常迂回的方法,根本不需要。
尽管如此,为了进一步提高效率,您应该将ymm0
的清除从循环中提升。也许是这样的?
#include <immintrin.h>
#define N 1000
void fillMatrixByZeros(float matrix[N][N]){
for (int k = 0; k < N; k += 8){
for (int i = 0; i < N; ++i){
asm volatile (
"vmovups %1, %0"
: "=m"(matrix[i][k])
: "x"(_mm256_setzero_ps())
: "memory"
);
}
}
}
请注意,只调用memset
可能会比手动内嵌程序集执行得更好。
https://stackoverflow.com/questions/70025215
复制