簡體   English   中英

指定編譯器可以使用的函數的 simd 級別

[英]specify simd level of a function that compiler can use

我編寫了一些代碼並使用帶有本機架構選項的 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 選擇部分。

簡單干凈的選擇

gcc 目標屬性可以像這樣立即使用

[[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) 將很樂意為您調用重載版本。 只要你定義了整個家庭。

這是我的解決方案。 我可以在支持 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 為我做了這ifunc ......)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM