2012-08-22 98 views
2

以下簡單代碼不會給出gcc 4.7.0的預期結果。這是正確的還是一個錯誤?omp減少和lambda函數

unsigned count_err(std::vector<unsigned> const&num, unsigned mask) 
    { 
    unsigned c=0; 
    // enables to reuse the lambda later (not in this simple example) 
    auto f = [&] (unsigned i) { if(i&mask) ++c; }; 
#pragma omp parallel for reduction(+:c) 
    for(unsigned i=0; i<num.size(); ++i) 
     f(num[i]); 
    return c; 
    } 

這將返回零:從lambda函數中減少c未完成。順便說一句,我期望得到的結果是由串行功能

unsigned count_ser(std::vector<unsigned> const&num, unsigned mask) 
    { 
    unsigned c=0; 
    auto f = [&] (unsigned i) { if(i&mask) ++c; }; 
    std::for_each(num.begin(),num.end(),f); 
    return c; 
    } 

以下實施方式中得到預期的結果(在兩種情況下,代碼定義做減少變量的增量移動到並行區域)返回

unsigned count_ok1(std::vector<unsigned> const&num, unsigned mask) 
    { 
    unsigned c=0; 
    auto f = [&] (unsigned i) -> bool { return i&mask; }; 
#pragma omp parallel for reduction(+:c) 
    for(unsigned i=0; i<num.size(); ++i) 
     if(f(num[i])) ++c; 
    return c; 
    } 

    unsigned count_ok2(std::vector<unsigned> const&num, unsigned mask) 
    { 
    unsigned c=0; 
#pragma omp parallel reduction(+:c) 
    { 
     auto f = [&] (unsigned i) { if(i&mask) ++c; }; 
#pragma omp for 
     for(unsigned i=0; i<num.size(); ++i) 
     f(num[i]); 
    } 
    return c; 
    } 

的事實是count_err()給出了不同的結果的編譯器錯誤或正確的嗎?

+0

不要嘗試將OpenMP與高級構造混合在一起,它肯定會失敗:OpenMP對於這一點太簡單了。它與語言整合的設計(但不是真的)是根本上有缺陷的。改爲手動減少。 –

+0

@KonradRudolph,lambdas是現有更詳細的C++功能的語法糖,因此它們使用函數編譯爲相同的代碼。 OpenMP僅爲#pragma部分中的代碼創建線程函數,並映射私有或共享變量。編譯器找出它並不是一件大事。另外它們都在編譯器中實現。 –

+0

@ lucas1024沒錯,這不是什麼大不了的事。更可惜的是它不能可靠地工作。 OpenMP甚至在最基本的C++構造塊上扼流(例如,嘗試通過'return'退出臨界區)。 –

回答

7

我認爲這不是一個編譯器錯誤。這是我的解釋。我想在你的第一個例子中,lambda表達式對全局變量c有參考價值。我們輸入for循環時創建了c的線程本地副本。所以線程增加了相同的全局變量(沒有任何同步)。當我們退出循環時,c(全部等於零,因爲lambda不知道它們)的線程本地副本被總結爲給你0. count_ok2版本的工作原理是因爲lambda表達式持有對本地c副本的引用。

+0

+1這是正確的答案。 –

+0

@HristoIliev同意這是解釋最有可能在技術上是正確的。但是,有點迂腐,這在法律上是正確的嗎?換句話說,openMP標準是否在減少的背景下談論本地副本?如果不是,那麼合法地只有一個全局變量,任何openMP實現以某種方式獲得減少,這必須減少並行區域內對這一個變量的所有引用(儘管標準可能會限制這一點)。在這種情況下,這是一個編譯器錯誤! – Walter

+2

@沃爾特,是的,它確實,否則我不會寫這個評論。更具體地說,§2.9.3.6的內容如下:_「reduction子句指定一個操作符和一個或多個列表項,對於每個列表項,在每個隱式任務中創建一個私人副本,並且對操作員進行適當的初始化。該區域中,使用指定的運算符使用私有副本的值更新原始列表項。「_這些私有副本所在的位置與實現有關,但它們與並行區域範圍之外的絕對副本完全不同。 –