You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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下均崩溃:

  1. 函数指针定义:
typedef __m256d (*FuncAVX)(const __m256d); // Windows下失效,Linux正常
  1. 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寄存器传递方式,导致栈和寄存器状态混乱,最终触发崩溃。

解决办法

  1. 显式指定调用约定:给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);
  1. 改用模板参数传递函数:避免使用函数指针或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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:34:52