禁用非Haswell处理器上的AVX2功能

3

我写了一些AVX2代码,可以在Haswell i7处理器上运行。同样的代码库也用在非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。感谢您的帮助。


你所说的“function”,是指“CPU指令”吗? - PlasmaHH
如果您正在使用gcc,您可能会对目标属性感兴趣。 - PlasmaHH
你可能严重高估了编译器在你的机器上运行时猜测用户机器外观的能力。它当然永远不可能是“编译器指令”。它必须是一个运行时测试,你的CRT将包装CPUID指令,告诉你处理器的真实外观。你没有留下任何线索,你使用的特定CRT很重要。 - Hans Passant
2个回答

18

除非必须,否则我认为将可执行文件分开不是一个好主意。在您的情况下,您可以制作一个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函数时,它首先调用product_dispatch设置函数指针fp为product_AVX2或product_SSE,然后从函数指针调用函数。第二次调用product时,它会直接跳转到product_AVX2或product_SSE。这样你就不需要有单独的可执行文件。

1
+1:好主意,但如果你有多个SIMD函数,这可能会有点麻烦 - 也许可以扩展为一个单一的函数指针表格,以减少重复的样板代码? - Paul R
2
编译和运行总是在同一台机器上进行,因此无需交叉复制二进制文件。另外,您的解决方案假定编译将在 AVX2 机器上完成(否则 AVX 文件将无法编译)。但无论如何,感谢您教给我新的知识。 - Alexandros
1
@PaulR,是的,我认为你是对的。我从Agner Fog那里得到了这个想法。在他的向量类库中的dispatch_example.cpp文件中有相关内容。我最初在Visual Studio中使用时遇到了一些问题,但现在它已经可以正常工作了。他在这本《优化C++手册》中写了10页关于编写调度程序的内容。Intel的调度程序会查找Intel ID,并在发现非Intel处理器时将函数指向一个次优的函数。因此,最好编写自己的调度程序。 - Z boson
1
我们不会忘记给你点赞。我觉得你忘记给 @PaulR 点赞了,他的回答非常出色 :-) - Alexandros
2
x264为每个具有可用于某些指令集的汇编版本的例程使用函数指针表。 if (cpu_has_sse3) {设置指向所有可用sse3例程的指针代码块;} if (cpu_has_avx) {设置指向所有可用avx例程的指针;}。 因此,您可以获得任何例程的最佳可用版本。 - Peter Cordes
显示剩余10条评论

5

如果您只想在编译时完成此操作,则可以执行以下操作:

#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 不会有问题。一旦测试完成,我将接受您的答案。 - Alexandros
1
您可能不需要显式添加-mavx2开关 - 可以使用例如gcc -dM -E -mmarch=native -mtune=native - < /dev/null | grep AVX检查是否已经隐含地为您执行此操作。 - Paul R
1
是的,它可以。所以,甚至不需要改变makefile。谢谢。 - Alexandros

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接