如何解决 SIMD 内联 asm 的左值问题,在 2D 数组中使用内存输出操作数?
How is the lvalue problem solved for SIMD inline asm with memory output operands in a 2D array?
我正在尝试编写一个函数,使用 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
是一个右值或类似的东西,所以它不能在那里使用。
经过谷歌搜索,我想到了两个解决方案:
第一个:
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"
);
}
}
}
这些功能正常工作。我想知道为什么。
为什么第一个函数没有左值问题?而第二个函数是怎么回事?
您不能分配给 matrix[i] + k
,因此它不是左值。 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 寻址模式。相反,它必须首先具体化寄存器中的地址。
你最后一个例子有点傻。在将 matrixPointer = matrix[i] + k
传递给内联汇编语句之前,它使用耦合的 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
的性能可能比手动滚动的内联汇编好得多。
我正在尝试编写一个函数,使用 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
是一个右值或类似的东西,所以它不能在那里使用。
经过谷歌搜索,我想到了两个解决方案:
第一个:
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"
);
}
}
}
这些功能正常工作。我想知道为什么。
为什么第一个函数没有左值问题?而第二个函数是怎么回事?
您不能分配给 matrix[i] + k
,因此它不是左值。 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 寻址模式。相反,它必须首先具体化寄存器中的地址。
你最后一个例子有点傻。在将 matrixPointer = matrix[i] + k
传递给内联汇编语句之前,它使用耦合的 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
的性能可能比手动滚动的内联汇编好得多。