为什么堆上创建的数组使用AVX指令执行GEMM时会触发段错误?
问题根因
你触发段错误的核心原因是AVX指令的对齐要求未满足:
- 你使用的
_mm256_load_pd、_mm256_store_pd均为对齐访问指令,要求操作的内存地址必须是32字节对齐,报错的汇编指令vmovapd就是对齐的256位浮点数传送指令,地址不对齐就会触发SIGSEGV。 - 栈上分配的二维数组默认会被编译器对齐到32字节边界,符合AVX的要求,因此运行正常。
- 你用普通
new分配的堆内存默认仅保证8字节对齐(匹配double类型的对齐要求),无法满足32字节对齐要求,因此触发段错误。
是否需要改用一维数组?
非常推荐改用一维连续数组,原因有两点:
- 便于实现对齐分配,能完美满足AVX指令的对齐要求
- 你当前实现的二维指针数组是逐行分配的,行与行之间内存不连续,会严重降低cache命中率,GEMM的性能会远低于连续一维数组的实现,这也是所有主流BLAS库的标准实现方式。
解决方案
方案1(推荐):使用对齐分配的一维数组
使用平台提供的对齐内存分配接口申请32字节对齐的连续内存:
// 分配示例,C11/C++11及以上可用aligned_alloc double* A = (double*)aligned_alloc(32, M * K * sizeof(double)); double* B = (double*)aligned_alloc(32, K * N * sizeof(double)); double* C = (double*)aligned_alloc(32, M * N * sizeof(double)); // 访问方式转换为一维下标 vec_2 = _mm256_load_pd(&B[j*K + k]); vec_res = _mm256_load_pd(&C[i*N + k]); // ... 运算逻辑不变 // 释放内存用free,不要用delete free(A); free(B); free(C);
Windows平台可替换aligned_alloc为_aligned_malloc,释放用_aligned_free。
方案2(临时兼容):改用非对齐AVX指令
如果你暂时不想改内存分配逻辑,可以把_mm256_load_pd换成_mm256_loadu_pd,_mm256_store_pd换成_mm256_storeu_pd,这两个指令支持非对齐访问,但性能会比对齐访问低10%~30%,仅适合临时调试使用。
方案3(保留二维下标访问):连续块+行指针映射
如果你想保留A[i][j]的二维访问方式,可以先申请一整块对齐的连续内存,再给行指针赋值:
void Malloc2DAligned(int height, int width, double**& Mat, double*& data) { Mat = new double*[height]; data = (double*)aligned_alloc(32, height * width * sizeof(double)); for(int i=0; i<height; i++) { Mat[i] = data + i * width; } } // 释放时先释放data,再释放行指针数组 free(data); delete[] Mat;
内容的提问来源于stack exchange,提问作者zjnyly
相关产品推荐
相关产品推荐

