为何_mm256_load_ps()在一台Haswell机型正常另一台却触发段错误?
看起来你遇到的问题是AVX内存加载指令的对齐要求在不同硬件上的行为差异导致的——虽然你用了posix_memalign分配了32字节对齐的内存,但有几个容易忽略的细节在ASUS的Haswell i7上触发了段错误,而Mac的Haswell i5可能因为系统或硬件的容错机制允许了不对齐的访问。
核心问题:内存访问的对齐违规
AVX的_mm256_load_ps指令要求加载的内存地址必须严格对齐到32字节,但你的代码中存在一个隐藏的对齐问题:
你的特征数组是按[特征0的所有样本, 特征1的所有样本, ...]的顺序存储的,每个特征的样本数是training_size=3089。计算每个特征的起始偏移时:
unsigned offset = j * prob_param.Size;
对应的字节偏移是offset * sizeof(float) = j * 3089 * 4。3089是奇数,乘以4后得到的字节数是j*12356,而12356除以32的余数是4(32*386=12352,12356-12352=4)。这意味着:
- 第0个特征的起始地址是对齐的(
feature_array + 0) - 第1个特征的起始地址是
feature_array + 12356,也就是对齐地址+4字节,未对齐到32字节 - 后续特征的起始地址偏移会越来越偏离32字节对齐要求
当你用_mm256_load_ps(&features[i + offset])加载第1个及以后的特征数据时,访问的地址是未对齐的,部分Haswell CPU会直接触发段错误,而Mac的CPU可能允许这种不对齐的AVX访问(或者系统开启了对齐容错)。
解决方案
你可以选择以下两种方案中的一种:
方案1:使用不对齐加载指令(简单快捷)
把_mm256_load_ps替换成_mm256_loadu_ps,这个指令支持未对齐的内存加载,虽然性能会有轻微损失,但能彻底解决对齐问题:
// 替换前 feat_vec = _mm256_load_ps(&features[i + offset]); // 替换后 feat_vec = _mm256_loadu_ps(&features[i + offset]);
方案2:调整数组布局,保证每个特征的起始地址对齐(性能最优)
如果想要保持AVX对齐加载的性能,可以调整数组的存储布局,让每个特征的起始地址都对齐到32字节:
- 计算每个特征需要的对齐后的字节数:
ALIGNED_FEAT_SIZE = ((prob_param.Size * sizeof(float) + 31) / 32) * 32 - 分配总内存时使用这个对齐后的大小:
size_t aligned_feat_size = ((training_size * sizeof(float) + 31) / 32) * 32; int ret = posix_memalign((void **) &feature_array, 32, aligned_feat_size * number_features); if (ret != 0) { perror("posix_memalign failed"); exit(EXIT_FAILURE); } - 访问特征时使用对齐后的偏移:
unsigned offset = j * (aligned_feat_size / sizeof(float));
额外的检查:确保内存分配成功
你的代码没有检查posix_memalign的返回值,如果内存分配失败,feature_array可能是未对齐的,也会触发段错误。建议添加错误检查:
int ret = posix_memalign((void **) &feature_array, 32, (size_t) training_size * number_features *sizeof(float)); if (ret != 0) { perror("posix_memalign failed"); exit(EXIT_FAILURE); }
编译选项确认
确保编译时启用了AVX支持,比如使用-mavx选项(GCC/Clang):
gcc -mavx -o scale scale.c common.c data.c -lm
内容的提问来源于stack exchange,提问作者A.SDR

