指定编译器可以使用的函数的simd级别

时间:2019-05-06 15:13:49

标签: c gcc simd

我编写了一些代码,并使用带有本机体系结构选项的gcc对其进行了编译。

通常,我可以接受此代码,然后在没有AVX2(仅AVX)的旧计算机上运行它,并且工作正常。但是,似乎编译器实际上正在发出AVX2指令(最终!),而不是我自己需要包含SIMD内部函数。

我想修改程序,以便同时支持两种途径(AVX2和非AVX2)。换句话说,我想要下面的伪代码。

if (AVX2){
   callAVX2Version();
}else if (AVX){
   callAVXVersion();
}else{
   callSSEVersion();
}

void callAVX2Version(){
#pragma gcc -mavx2
}

void callAVXVersion(){
#pragma gcc -mavx
}

我知道如何执行运行时检测部分,我的问题是是否可以执行功能特定的SIMD选择部分。

2 个答案:

答案 0 :(得分:2)

简单干净的选项

gcc target属性可以像这样无法使用

[[gnu::target("avx")]]
void foo(){}

[[gnu::target("default")]]
void foo(){}

[[gnu::target("arch=sandybridge")]]
void foo(){}

呼叫随即变为

foo();

此选项无需使用其他函数名称。 例如,如果您签出godbolt,将会看到它为您创建了@gnu_indirect_function。首先将其设置为.resolver函数。它将读取__cpu_model,以找出可以使用的内容,并将间接函数设置为该指针,以便所有后续调用都将是简单的间接函数。 简单吧。但是您可能需要与原始代码保持更近的距离,因此还有其他方法

功能切换

如果您确实需要像原始示例中那样进行功能切换。可以使用以下内容。它使用了措辞不错的buildtins,因此很明显,您可以切换到体系结构

[[gnu::target("avx")]]
int foo_avx(){ return 1;}

[[gnu::target("default")]]
int foo(){return 0;}

[[gnu::target("arch=sandybridge")]]
int foo_sandy(){return 2;}

int main ()
{
    if (__builtin_cpu_is("sandybridge"))
        return foo_sandy();
    else if (__builtin_cpu_supports("avx"))
        return  foo_avx();
    else
        return foo();
}

定义您自己的间接函数

由于其他人或平台更加冗长的原因,间接功能可能不是受支持的用例。下面是一种与第一个选项相同的方法,但是全部使用c ++代码。使用静态局部函数指针。这意味着您可以根据自己的喜好或在不支持内置的情况下为目标指定优先级。您可以自己提供。

auto foo()
{
    using T = decltype(foo_default);
    static T* pointer = nullptr;
    //static int (*pointer)() = nullptr; 
    if (pointer == nullptr)
    {
    if (__builtin_cpu_is("sandybridge"))
        pointer = &foo_sandy;
    else if (__builtin_cpu_supports("avx"))
        pointer = &foo_avx;
    else
        pointer = &foo_default;        
    }
    return pointer();
};

作为奖励记录

以下godbolt上的模板化示例使用了template<class ... Ts> 处理功能的重载 也就是说,如果您定义callXXXVersion(int)族,那么foo(int)会为您高兴地调用重载版本。只要您定义了整个家庭。

答案 1 :(得分:1)

这是我的解决方案。我可以使用AVX2支持进行编译,并且仍然可以在我的Ivy Bridge处理器(仅适用于AVX)上运行。

功能是:

__attribute__((target("arch=haswell")))
void fir_avx2_std(STD_DEF){
    STD_FIR;    
}

__attribute__((target("arch=sandybridge")))
void fir_avx_std(STD_DEF){
    STD_FIR;
}

//Use default - no arch specified
void fir_sse_std(STD_DEF){
    STD_FIR;    
}

电话是:

if (s.HW_AVX2 && s.OS_AVX){
    fir_avx2_std(STD_Call);
}else if(s.HW_AVX && s.OS_AVX){
    fir_avx_std(STD_Call);
}else{
    fir_sse_std(STD_Call);
}   

s是根据我在网上找到的一些代码(https://github.com/Mysticial/FeatureDetector

填充的结构

STD_FIR是带有实际代码的宏,针对每种体系结构,优化的方式有所不同。

我正在使用-std=c11 -ffast-math -O3

进行编译

我最初也有-march=haswell,但这引起了问题。

注意,我不确定这是否是最佳目标细分... 另外,我尝试使target_clones正常工作,但是却遇到了需要ifunc的错误(我以为gcc为我做了这件事……)