Windows下C++ AVX2函数指针/std::function失效,Linux正常
问题:通过函数指针或std::function传递AVX2函数时Windows下崩溃,Linux运行正常
现象概述
直接调用AVX2函数在Linux和Windows下均正常,但通过函数指针或std::function传递这类函数并间接调用时,Windows会崩溃,Linux无异常。
测试用AVX2函数
// 简单测试函数:将向量乘以2 __m256d test_simple_AVX2(const __m256d x) { const __m256d two = _mm256_set1_pd(2.0); const __m256d res = _mm256_mul_pd(x, two); return res; } // 对标用的标量版本 double test_simple_double(const double x) { const double res = 2.0*x; return res; }
通用模板函数(用于接收并调用传递的函数)
template <typename T> inline void TEST_fn_AVX2_row_or_col_vector(Eigen::Ref<T> x_Ref, FuncAVX fn_AVX, FuncDouble fn_double) { const int N = x_Ref.size(); const int vect_size = 4; const double vect_siz_dbl = static_cast<double>(vect_size); const double N_dbl = static_cast<double>(N); const int N_divisible_by_vect_size = std::floor(N_dbl / vect_siz_dbl) * vect_size; Eigen::Matrix<double, -1, 1> x_tail = Eigen::Matrix<double, -1, 1>::Zero(vect_size); // 最后4个元素 { int counter = 0; for (int i = N - vect_size; i < N; ++i) { x_tail(counter) = x_Ref(i); counter += 1; } } if (N >= vect_size) { alignas(32) double buffer[4]; // AVX操作使用对齐缓冲区 for (int i = 0; i + vect_size <= N_divisible_by_vect_size; i += vect_size) { // 复制数据到对齐缓冲区 for(int j = 0; j < vect_size; j++) { buffer[j] = x_Ref(i + j); } const __m256d AVX_array = _mm256_load_pd(buffer); const __m256d AVX_array_out = fn_AVX(AVX_array); // 崩溃发生在此处:调用fn_AVX时 // 注:如果直接在这里写AVX操作(不通过fn_AVX调用),Windows下正常工作: // const __m256d two = _mm256_set1_pd(2.0); // const __m256d AVX_array_out = _mm256_mul_pd(AVX_array, two); _mm256_store_pd(buffer, AVX_array_out); // 复制结果回Eigen容器 for(int j = 0; j < vect_size; j++) { x_Ref(i + j) = buffer[j]; } } if (N_divisible_by_vect_size != N) { // 处理剩余元素 int counter = 0; for (int i = N - vect_size; i < N; ++i) { x_Ref(i) = fn_double(x_tail(counter)); counter += 1; } } } else { // 元素数量小于4时,全用标量操作处理 for (int i = 0; i < N; ++i) { x_Ref(i) = fn_double(x_Ref(i)); } } }
尝试过的函数类型定义
两种方式在Linux下正常,Windows下均崩溃:
- 函数指针定义:
typedef __m256d (*FuncAVX)(const __m256d); // Windows下失效,Linux正常
- std::function定义:
typedef std::function<__m256d(const __m256d)> FuncAVX; // Windows下失效,Linux正常
额外信息
- 即使改用非对齐AVX intrinsics(
_mm256_loadu_pd和_mm256_storeu_pd),问题依旧。 - 开发环境:通过Rcpp使用C++,编译器为g++,编译参数:
-O3 -march=znver3 -mtune=znver3 -fPIC -D_REENTRANT -DSTAN_THREADS -pthread -fpermissive -mfma -mavx -mavx2 -flarge-source-files
问题原因分析
这是Windows下g++(MinGW)的调用约定不匹配导致的:
- Linux使用的System V AMD64 ABI会将
__m256d这类向量参数/返回值放在YMM寄存器中传递; - Windows的x64调用约定(MSVC ABI)中,向量类型的参数需要通过栈传递,返回值则放在YMM0寄存器;
- 你使用的g++(MinGW)在Windows下,直接调用AVX2函数时编译器能自动匹配正确的调用约定,但通过函数指针/std::function间接调用时,错误地使用了Linux的System V寄存器传递方式,导致栈和寄存器状态混乱,最终触发崩溃。
解决办法
- 显式指定调用约定:给AVX2函数和函数指针都加上
__attribute__((ms_abi)),强制使用Windows的x64调用约定:
// 定义AVX2函数时 __m256d __attribute__((ms_abi)) test_simple_AVX2(const __m256d x) { // ...函数内容不变 } // 函数指针定义时 typedef __m256d (__attribute__((ms_abi)) *FuncAVX)(const __m256d);
- 改用模板参数传递函数:避免使用函数指针或std::function,直接将函数作为模板参数传递,让编译器在编译期直接展开调用,绕开调用约定的问题:
template <typename T, typename AVXFunc, typename ScalarFunc> inline void TEST_fn_AVX2_row_or_col_vector(Eigen::Ref<T> x_Ref, AVXFunc fn_AVX, ScalarFunc fn_double) { // 函数内容不变 } // 调用时直接传函数名 TEST_fn_AVX2_row_or_col_vector(x_ref, test_simple_AVX2, test_simple_double);
内容的提问来源于stack exchange,提问作者E_1996
相关产品推荐
相关产品推荐

