每个模板实例化的 GCC/Clang 函数属性

use*_*363 14 c++ gcc clang template-specialization function-attributes

我有一些手工向量化的 C++ 代码,我正在尝试通过函数多版本化制作可分发的二进制文件。由于代码对不同指令集(SSE2、AVX2、AVX512)使用 SIMD 内在函数,因此它使用模板专业化来决定使用哪些内在函数。

整体结构大致如下:

template <unsigned W, unsigned N> struct SIMD {};  // SIMD abstraction

template <> struct SIMD<128, 8> {  // specialization for specific dimensions
  using Vec = __m128i;
  static always_inline Vec add(Vec a, Vec b) { return _mm_add_epi8(a, b); }
  ...  // many other SIMD methods
};

... // many other dimension specializations for different instruction sets

template <unsigned W, unsigned N> class Worker {
  void doComputation(int x) {
    using S = SIMD<W, N>;
    ... // do computations using S:: methods
  }
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是我需要不同的实例来Worker具有不同的属性,因为每个实例都针对不同的指令集。像这样的东西:

template __attribute__((target("avx2")))     void Worker<256, 8>::doComputation(int x);
template __attribute__((target("avx512bw"))) void Worker<512, 8>::doComputation(int x);
...
Run Code Online (Sandbox Code Playgroud)

以便为不同的目标编译这些不同的实例。然而,这仍然会在 Clang 上产生错误:

错误:always_inline 函数“add”需要目标功能“avx2”,但是
       将被内联到已编译的函数“doComputation”中
       不支持“avx2”

如果我用它注释原始方法,__attribute__((target("avx2,avx512")))它会编译,但在没有 AVX-512 支持的情况下在运行时执行非法硬件指令,所以我想我使用上面注释的专业化的直觉不起作用。

有没有办法用 Clang 或 GCC 使用函数属性来表达这一点?

小智 1

我发现尝试[[noreturn]]在同一函数模板的不同专业化上使用不同的属性(甚至是标准化的属性)会导致一段糟糕的时光。我的解决方案是添加一层词法间接:将您的实现拆分为、WorkerSSE2、等,然后根据运行时检测到的功能在它们之间进行选择。如果您尝试将所有不同架构的实现预编译为单个“通用”二进制文件,同时使用通用代码将它们缝合在一起,那么在控制流中的某个时刻,您需要一个运行时间接来选择要分派哪个实现。WorkerAVX2WorkerAVX512BWWorker

您可以在每次调用包装函数时通过函数表来完成此操作,或者设置一堆函数指针,这些指针在程序首次加载时设置一次(这就是大多数 OpenGL 实现的工作方式)。