2016-07-13 33 views
11

這是一個簡單的C++程序使用valarrays:爲什麼GCC優化不適用於valarrays?

#include <iostream> 
#include <valarray> 

int main() { 
    using ratios_t = std::valarray<float>; 

    ratios_t a{0.5, 1, 2}; 
    const auto& res (ratios_t::value_type(256)/a); 
    for(const auto& r : ratios_t{res}) 
     std::cout << r << " " << std::endl; 
    return 0; 
} 

如果我編譯並這樣運行:

g++ -O0 main.cpp && ./a.out 

如預期的輸出:

512 256 128 

然而,如果我編譯並像這樣運行:

g++ -O3 main.cpp && ./a.out 

輸出是:如果我使用-O1優化參數

0 0 0 

同樣的情況。

GCC版本(最新的的Archlinux):

$ g++ --version 
g++ (GCC) 6.1.1 20160707 

不過,如果我嘗試用鏗鏘,既

clang++ -std=gnu++14 -O0 main.cpp && ./a.out 

clang++ -std=gnu++14 -O3 main.cpp && ./a.out 

產生同樣正確的結果:

512 256 128 

鏘的版本是:

$ clang++ --version 
clang version 3.8.0 (tags/RELEASE_380/final) 

我也Debian的,其中可執行產生正確的結果,試圖用GCC 4.9.2。

這是GCC中的一個可能的錯誤還是我做錯了什麼?任何人都可以重現嗎?

編輯:我設法在Mac OS上的GCC 6的自制版本上也重現該問題。

+0

使用http://melpon.org/wandbox它似乎從4.9.3到5.1的行爲更改。 – NathanOliver

+0

不幸的是,在我的代碼庫中,我甚至在GCC 4.9.3上也設法重現了類似的問題(但是使用了uint32_t),但是它在最小的例子中起作用。我正在調查... – DoDo

回答

6

valarrayauto混合不好。

這就形成了一個臨時的物體,然後應用operator/它:

const auto& res (ratios_t::value_type(256)/a); 

的的libstdC++ valarray使用表達式模板以便operator/返回指原始參數和懶惰地評估它們的輕質對象。您使用const auto&這會導致將表達式模板綁定到引用,但不會延長表達式模板引用的臨時表的生命週期,因此,當評估發生時臨時已經超出範圍,並且其內存已經重複使用。

這將正常工作,如果你這樣做:

ratios_t res = ratios_t::value_type(256)/a; 
+0

謝謝你的詳細解答。我會接受它。我只想指出,即使我寫'auto res(ratios_t :: value_type(256)/ a)',我仍然可以得到相同的結果,並打開優化。但是,您的建議可行(即不使用'auto')。使用clang和msvc原始代碼正常工作。 – DoDo

+0

是的,因爲'auto'會推導出包含過期臨時引用的表達式模板的類型。您需要明確創建一個'valarray',在臨時消失之前強制執行評估。正如我所說,「auto」和「valarray」混合不好。 –

+0

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57997是一個非常類似的問題。 –

3

這是草率地實施使用懶評價operator/ (const T& val, const std::valarray<T>& rhs)(超過valarrays並極有可能其他運營商)的結果:

#include <iostream> 
#include <valarray> 

int main() { 
    using ratios_t = std::valarray<float>; 

    ratios_t a{0.5, 1, 2}; 
    float x = 256; 
    const auto& res (x/a); 
    // x = 512; // <-- uncommenting this line affects the output 
    for(const auto& r : ratios_t{res}) 
     std::cout << r << " "; 
    return 0; 
} 

隨着註釋掉「x = 512」行,輸出是

512 256 128 

取消註釋該行和輸出更改爲

1024 512 256 

由於在您的示例中,除法運算符的左側參數是臨時的,因此結果未定義。

UPDATE

作爲Jonathan Wakely正確pointed out,懶惰評價基於實現變得在這個例子中一個問題由於auto用法。

+0

謝謝你的回答。喬納森是第一個,所以我接受了他的回答,但你的信息也很豐富。太糟糕了,不讓你接受多個答案:-)。 – DoDo

相關問題