2017-02-14 73 views
1

在下面的模板函數示例中,for循環內部的中央if保證被優化,只保留使用的指令?帶模板布爾參數的函數:保證被優化?

如果不能保證優化(在GCC 4,MSVC 2013和llvm 8.0中),有什麼選擇,最多使用C++ 11?

請注意,這個函數沒有任何用處,我知道這個特定的函數可以通過幾種方式進行優化等等。但我想要關注的是如何在生成代碼中使用bool模板參數。

template <bool IsMin> 
float IterateOverArray(float* vals, int arraySize) { 

    float ret = (IsMin ? std::numeric_limits<float>::max() : -std::numeric_limits<float>::max()); 

    for (int x = 0; x < arraySize; x++) { 
     // Is this code optimized by the compiler to skip the unnecessary if? 
     if (isMin) { 
      if (ret > vals[x]) ret = vals[x]; 
     } else { 
      if (ret < vals[x]) ret = vals[x]; 
     } 
    } 

    return val; 

} 
+2

我認爲這不是優化,因爲在C++中幾乎沒有保證的優化。不過,我希望合理的編譯器能夠做到這一點,因爲它可能會首先生成函數代碼並優化它,並且優化'if(true)'應該很容易。 – yeputons

+0

不,但在C++ 17中,你可以使用'如果constexpr(isMin){'。 – flyx

+0

@yeputons ok,編輯我的問題。只有C++ 11有什麼好的選擇? – manatttta

回答

4

理論上沒有。 C++標準允許編譯器不僅愚蠢,而且徹頭徹尾的敵對。只要抽象機器的行爲保持不變,它就可以無條件地注入無用的東西。

實際上,是的。死代碼消除和常量分支檢測很容易,我檢查過的每一個編譯器都消除了if分支。

請注意,兩個分支都是在一個被刪除前編譯的,因此它們都必須是完全有效的代碼。輸出程序集表現爲「好像」兩個分支都存在,但分支指令(和不可達代碼)不是抽象機器行爲的可觀察特徵。

當然,如果不進行優化,可能會留下分支和死代碼,因此您可以使用調試器將指令指針移動到「死代碼」。

1

不保證它會被優化掉。儘管這是一個編譯時間常數,但它有一個很好的機會。

這就是說C++ 17給了我們if constexpr它只會編譯通過檢查的代碼。如果你想要保證,那麼我建議你改用這個功能。

在C++之前17如果你只想編譯代碼的一部分,你需要專門化這個函數,並且只寫與那個特化相關的代碼。

1

既然你問到在C++ 11這裏的替代是一個:

float IterateOverArrayImpl(float* vals, int arraySize, std::false_type) 
{ 
    float ret = -std::numeric_limits<float>::max(); 
    for (int x = 0; x < arraySize; x++) { 
     if (ret < vals[x]) 
      ret = vals[x]; 
    } 
    return ret; 
} 

float IterateOverArrayImpl(float* vals, int arraySize, std::true_type) 
{ 
    float ret = std::numeric_limits<float>::max(); 
    for (int x = 0; x < arraySize; x++) { 
     if (ret > vals[x]) 
      ret = vals[x]; 
    } 
    return ret; 
} 

template <bool IsMin> 
float IterateOverArray(float* vals, int arraySize) { 

    return IterateOverArrayImpl(vals, arraySize, std::integral_constant<bool, IsMin>()); 

} 

你可以看到它在現場here

這個想法是使用函數重載來處理測試。