C++ AVX2 SIMD编程中_mm256_load_ps加载触发段错误问题
AVX2
_mm256_load_ps 触发段错误问题排查 问题复现代码
运行如下AVX2并行求和实现:
template<SumAlgorithm algorithm_t, typename iterator_t, typename sum_t = typename std::iterator_traits<iterator_t>::value_type> sum_t avx2_sum(iterator_t begin, iterator_t end) noexcept requires std::is_same<sum_t, float>::value && (algorithm_t == SumAlgorithm::Normal) { // SIMD并行求和阶段 auto running_sums = _mm256_set1_ps(0); long iterator_skip = 256/(8*sizeof(sum_t)); for (; begin < end; begin += iterator_skip){ std::cout << "HERE\n"; __m256 tmp = _mm256_load_ps(begin); running_sums = _mm256_add_ps(tmp, running_sums); } // 结果串行归约 running_sums = _mm256_add_ps(running_sums, _mm256_permute2f128_ps(running_sums, running_sums, 1)); running_sums = _mm256_hadd_ps(running_sums, running_sums); running_sums = _mm256_hadd_ps(running_sums, running_sums); return _mm256_cvtss_f32(running_sums); }
调用代码如下:
constexpr unsigned int num_elements = 4096*4096; constexpr unsigned int alignment = 32; alignas(alignment) std::vector<float> float_vec(num_elements, 1.0); std::cout << "avx2<float, Normal>: " << accumulators::avx2_sum<algo::Normal>(&(*float_vec.begin()), &(*float_vec.end())) <<"\n";
程序首次进入循环执行到_mm256_load_ps时触发段错误。
已验证现象
- 对传入的
begin指针做解引用操作,可正常读取到有效值1.0 - 代码已对
std::vector对象声明32字节对齐,故障仍存在
故障根因
alignas修饰符仅会保证栈上的std::vector对象自身(即vector的内部控制块,存储堆内存指针、元素计数、容量等成员)按照32字节对齐,不会影响vector在堆上分配的元素存储内存的对齐属性。_mm256_load_ps是严格对齐的AVX2加载指令,要求访存地址必须32字节对齐,否则就会触发内存访问异常。普通标量解引用仅要求地址指向合法可访问内存,不要求32字节对齐,因此会出现单元素读取正常、SIMD加载报错的现象。
修复方案
- 方案一(性能最优):为
std::vector配置符合C++标准的对齐内存分配器,让vector申请堆内存时主动分配满足32字节对齐要求的空间存储元素,适配对齐加载指令的要求。 - 方案二(实现简单):将对齐加载指令
_mm256_load_ps替换为支持非对齐地址访问的_mm256_loadu_ps,无需修改vector的分配逻辑,但非对齐加载在部分场景下性能略低于对齐加载。
内容的提问来源于stack exchange,提问作者M46f988b814
相关产品推荐
相关产品推荐

