【问题标题】:Disable AVX2 functions on non-Haswell processors在非 Haswell 处理器上禁用 AVX2 功能
【发布时间】:2025-12-05 00:40:01
【问题描述】:

我编写了一些在 Haswell i7 处理器上运行的 AVX2 代码。相同的代码库也用于非 Haswell 处理器,其中相同的代码应替换为其 SSE 等效项。我想知道编译器是否有办法忽略非 Haswell 处理器上的 AVX2 指令。我需要类似的东西:

public void useSSEorAVX(...){
    IF (compiler directive detected AVX2)
        AVX2 code (this part is ready)
    ELSE
        SSE code  (this part is also ready)
    }
}

现在我正在编译之前注释掉相关代码,但必须有一些更有效的方法来做到这一点。我正在使用 Ubuntu 和 gcc。感谢您的帮助。

【问题讨论】:

  • “函数”是指“cpu指令”吗?
  • 如果您使用 gcc,您可能会对 target 属性感兴趣。
  • 你可能严重高估了编译器的智能,运行在你的机器上,去猜测用户机器的样子。它当然永远不会是“编译器指令”。它必须是运行时测试,您的 CRT 将包装 CPUID 指令,告诉您处理器的真实外观。您没有留下任何面包屑,您使用的具体 CRT 很重要。

标签: c++ sse avx2


【解决方案1】:

如果您只想在编译时执行此操作,那么您可以这样做:

#ifdef __AVX2__
    // AVX2 code
#elif __SSE__
    // SSE code
#else
    // scalar code
#endif

请注意,当您使用 gcc -mavx2 ... 编译时,__AVX2__ 会自动定义。 __SSE__ 也是如此。 (另请注意,您可以使用咒语gcc -dM -E -mavx2 - < /dev/null 检查编译器为任何给定命令行切换预定义的内容。)

如果您想进行运行时调度,那么这会稍微复杂一些。

【讨论】:

  • +1 现在,我的 makefile 上有 -march=native 和 -mtune=native 但在 Haswell 处理器上使用的 makefile 上添加 -mavx2 没有问题。一旦测试,我会接受你的答案
  • 您可能不需要显式添加 -mavx2 开关 - 检查 -march=native -mtune=native 是否已经为您隐式执行此操作,例如使用gcc -dM -E -mmarch=native -mtune=native - < /dev/null | grep AVX.
  • 是的。因此,甚至不需要更改生成文件。谢谢
【解决方案2】:

我不认为制作单独的可执行文件是一个好主意,除非你必须这样做。在您的情况下,您可以制作一个 CPU 调度程序。我最近为 GCC 和 Visual Studio 做了这个。

假设您有一个名为 product 的函数,用于 SSE 和 AVX。您将 SSE 版本放在文件 product_SSE.cpp 中,将 AVX2 版本放在文件 product_AVX2.cpp 中。您分别编译每一个(例如使用-msse2-mavx2)。然后像这样制作一个模块:

extern "C" void product_SSE(float *a, float *b, float *c, int n);
extern "C" void product_AVX2(float *a, float *b, float *c, int n); 
           void product_dispatch(float *a, float *b, float *c, int n); 
void (*fp)(float* a, float *b, float *c, int n) = product_dispatch;

inline void product_dispatch(float *a, float *b, float *c, int n) {
    int iset = instrset_detect();
    if(iset==8) {
        fp = product_AVX2
    }
    else {
        fp = product_SSE
    }
    fp(a,b,c,n);
}

inline void product(float *a, float *b, float*c, int bs) {
    fp(a,b,c,n);
}

您使用较低的通用指令集(例如使用 SSE2)编译该模块。现在,当您调用产品时,它首先调用product_dispatch 将函数指针fp 设置为product_AVX2product_SSE,然后从函数指针调用函数。第二次调用product它会直接跳转到product_AVX2product_SSE。这样您就不必拥有单独的可执行文件。

【讨论】:

  • +1: 好主意,但如果你有多个 SIMD 函数会有点痛苦 - 也许它可以扩展到单个函数指针 table 以减少重复样板代码的数量?
  • 编译和运行总是在同一台机器上。因此,无需交叉复制二进制文件。此外,您的解决方案假定编译将在 AVX2 机器上完成(否则 AVX 文件将无法编译)。但是无论如何要为我学习新事物+1
  • @PaulR,是的,我认为你是对的。我从 Agner Fog 那里得到了这个想法。它在他的向量类库中的文件 dispatch_example.cpp 中。我最初在 Visual Studio 中工作时遇到了一些麻烦,但现在工作正常。他在这本 Optimizing C++ 手册中有 10 页关于编写调度程序的内容。英特尔的调度程序查找英特尔 ID 并在找到非英特尔处理器时将函数指向次优的。所以最好自己写dispatcher。
  • 我们不会忘记给您 +1。我想你忘了给 @PaulR 你的 +1 以感谢他的出色回答 :-)
  • x264 对每个具有可用于某些指令集的 asm 版本的例程使用函数指针表。 if (cpu_has_sse3) { block of code setting pointers to all available sse3 routines; } if (cpu_has_avx) { set pointers to all available avx routines; }。因此,您可以获得任何例程的最佳可用版本。