使用-mavx(或-march = sandybridge - > skylake)使用clang编译时,下面的两个函数似乎会导致分段错误。
void _mm256_mul_double_intrin(double* a, double* b, int N)
{
int nb_iters = N / ( sizeof(__m256d) / sizeof(double) );
__m256d* l = (__m256d*)a;
__m256d* r = (__m256d*)b;
for (int i = 0; i < nb_iters; ++i, ++l, ++r)
_mm256_store_pd((double *)l, _mm256_mul_pd(*l, *r));
}
void _mm256_mul_double(double* a, double* b, int N)
{
int nb_iters = N / ( sizeof(__m256d) / sizeof(double) );
__m256d* l = (__m256d*)a;
__m256d* r = (__m256d*)b;
for (int i = 0; i < nb_iters; ++i, ++l, ++r)
__asm__(
"vmulpd %[r], %[l], %[l] \t\n"
: [l] "+x" (*l)
: [r] "m" (*r)
:
);
}
当N是4的2倍或更多(ymm寄存器宽度/双倍宽度)时,clang编译代码有时会导致分段错误。 (参见下面的wandbox链接)
GCC编译的代码似乎很好。
wandbox.org/permlink/kex4e3lRCKfPAq2J
**我在stackoverflow.com上找到了原始源代码
答案 0 :(得分:4)
答案就在你与Godbolt联系的asm中:
gcc使用andq $-32, %rsp
将堆栈对齐32,因此代码中所有对齐所需的加载和存储都不会出错。 (取消引用__m256d*
和_mm256_store_pd
而不是_mm256_storeu_pd
)。 AVX指令通常不需要对齐,但是对齐移动指令(如vmovapd
)可以。
这对于gcc来说只有 ,因为你的测试用例允许在__m256d
和double a[]
上使用double b[]
操作的函数内联到分配函数的函数中堆栈上的数组。
例如:
void ext(double *);
void foo(void) {
double tmp [1024];
ext(tmp);
}
编译为简单分配,没有过度对齐堆栈。
subq $8200, %rsp
movq %rsp, %rdi
call ext(double*)
addq $8200, %rsp
ret
x86-64 SysV ABI仅需要16B堆栈对齐。 (并且gcc不会选择维护更多。)因此,如果ext()
实际上是您需要32位字节对齐double*
的函数之一,那么它就会出错。
gcc不知道32B对齐会对ext()
产生性能提升,因此它不会花费指令来对齐所有自动存储阵列。如果存在正确性问题,那就是你的错!
即使在内联之后,Clang也没有进行任何对齐,只是使用subq $248, %rsp
在堆栈上保留空间。因此,即使在您的测试用例中,堆栈地址空间随机化也只会在一半的时间内为您提供32B对齐的堆栈。
如果使用alignas(32) double a[]
,则需要所有编译器对齐数组。 (alignas
不适用于动态存储,例如new
或malloc
,但它适用于自动和静态数组。有关动态,请参阅How to solve the 32-byte-alignment issue for AVX load/store operations?)。
答案 1 :(得分:2)
c
代替_mm256_loadu_pd(r)
和{{1而不是*r
和_mm256_loadu_pd(l)
来存储变量。