2017-09-05 70 views
1

我必須調用f(0)f(1)傳遞常數。
參數(01)被用於僅在開關的情況下(一個或多個)。
如何強制/導向編譯器優化出的switch-case(從「昂貴」到「便宜」下面版本)儘可能?優化開關(x)中,x是作爲參數

godbolt demo,開關情況下的未優化的。

例子:昂貴

int f(int n) { 
    switch(n) { 
     case 0: { 
      return 5; 
     };break; 

     case 1: { 
      return 10; 
     };break; 
    } 

    return 15; 
} 

int main(){ 
    f(0); 
} 

例子:價格便宜(我的夢想)

int f0(){ 
    return 5; 
} 

int f1(){ 
    return 10; 
} 

int main(){ 
    f0(); 
} 

的更多信息: -

在實際情況中,有超過只有0和1 - 他們是枚舉類。
參數總是恆定在用戶的方面例如f(CALLBACK_BEGIN)f(CALLBACK_END)

爲什麼我不能只是f0()/ f1()?

我想將它分組爲單個函數,因爲我有時想創建一個傳遞函數。這是比較容易維護,如果我可以編寫它像: -

int g(int n){ .... } 
int f(int n){ return g(n); } 

這是比較容易維護比: -

int g0(){ .... } int g1(){ .... } 
int f0(){ return g0(); } 
int f1(){ return g1(); } 

我也喜歡避免模板,所以我不能使用的解決方案Optimize Template replacement of a switch。我的理由是: -

  • 模板必須在標頭中實現。
  • 我需要它在.cpp,所以我不得不間接它到另一個非模板函數。
    它變髒非常快。

過早優化?

在我的情況下,它被稱爲60 * 10000 +次/秒。

編輯

我誤解了godbolt演示的結果。它實際上是優化的。
(感謝M.M伯努瓦指出來。)

EDIT2

接收兩個偉大的答案後,我測試了一下,發現的Visual C++是非常聰明的。
它可以優化的東西,如: -

int f(int p1,int p2){ 
    if(p1==0 && p2==1){ //zero cost 

    } 
} 
f(0,1); //inside main 

在現實情況下,有功能的間接3-5層,但Visual C++仍然可以找到!

的結果是類似的職位是一致的:Constant condition in a loop: compiler optimization

+0

你godbolt顯示呼叫到f(0)是優化離開 –

+0

@Benoît可恥的我!我對編譯語言(彙編)非常陌生。 – cppBeginner

+0

只要確保f被定義爲內嵌在你的頭部之一中。 –

回答

3

在您的演示通話f(0);main被優化掉了,你可以從組裝看到main

main: 
     mov  r0, #0 
     bx  lr 

代碼爲f(int)看起來已經非常優化了,我認爲調用一個函數而不是發佈一個彙編指令會更不理想。

+0

哦,不! '_GLOBAL__sub_I__Z1fi:'不相關? – cppBeginner

+0

@cppBeginner正在初始化iostreams庫(如果你取出'#include ',它將會消失) –

+0

謝謝。順便說一下,它通常會被優化出來嗎?即我可以依靠這種類型的優化有多少? – cppBeginner

8

一個簡單的解決方案是讓你的函數constexpr,它可以緩解優化很多。

// v--- that 
constexpr int f(int n) { 
    switch(n) { 
     case 0: { 
      return 5; 
     };break; 

     case 1: { 
      return 10; 
     };break; 
    } 

    return 15; 
} 

這使得函數在編譯時可調用。如果傳遞的參數是constexpr值,則可以在編譯過程中由編譯器執行該函數。由於您將枚舉值作爲參數傳遞,因此很可能在編譯時執行該函數。

如果您重功能需要一些運行時的值,試圖分析出可能被constexpr標記的部分,也許還可以利用模板(他們真的是使代碼更快有用!)

constexpr int const_part_of_f(int n) { 
    switch(n) { 
     case 0: { 
      return 5; 
     };break; 

     case 1: { 
      return 10; 
     };break; 
    } 
} 

template<int n> 
int f() { 
    if (get_runtime_value()) { 
     // Since `n` is a compile time constant, the result of `const_part_of_f` is 
     // evaluated at compile time, even if `f` is not a constexpr function. 
     return const_part_of_f(n) 
    } 

    return 15; 
} 

如果你真的想幫助優化器,避免過多的內存分配。例如,如果您需要編譯時已知的特定大小的數組,請使用std::array而不是std::vector

正如其他用戶指出的那樣,二進制文件擴展名是初始化iostream全局變量。但是,這並不否認constexpr函數更容易被編譯器優化的事實。

+0

我剛剛閱讀http://en.cppreference.com/w/cpp/language/constexpr,並試圖找到這種方法('constexpr')的限制,但沒有發現。更復雜的東西仍然行得通(http://coliru.stacked-crooked.com/a/826109f921f6089d)。是否有任何真正的限制(除了殘疾使用「虛擬功能」),我應該知道? – cppBeginner

+0

@cppBeginner你不能濫用未定義的行爲,在constexpr函數中使用的對象不能有一個不平凡的析構函數,你不能使用免費商店(堆分配)。另外,所有變量必須在創建時初始化。這些是我所知道的主要限制。 –

+0

@cppBeginner也請注意,constexpr函數仍然可以在運行時調用。如果將非常量參數傳遞給constexpr函數,則會在運行時靜默地評估該函數。 –

1

有了模板,你可能是你的夢想近了

template <int N> int f(); 

template <> int f<0>() { return 5; } 
template <> int f<1>() { return 10; } 

int main(){ 
    f<0>(); 
} 

或C++ 17,與constexpr如果

template <int N> int f() 
{ 
    static_assert(N == 0 || N == 1); 

    if constexpr (N == 0) { 
     return 5; 
    } else if constexpr (N == 1) { 
     return 10; 
    } 
} 
+0

@cppBeginner:如果正確完成,實現可以進入cpp。 – Jarod42

+0

查看*「顯式實例化」*,你不需要中間'g()'。順便說一句,完全專業化(如第一個例子),即使沒有'main',你也已經實例化了'f <0>'/'f <1>'。 – Jarod42

+0

完全專業化確實可以在.cpp中定義的.h中聲明,這完全是因爲它們不再依賴於模板參數。 – MSalters

相關問題