2011-09-16 41 views
0

我有以下代碼在我有的編譯器(xlC和gcc)上工作,但我不知道它是否完全兼容(我在OpenMP 3.0規範中沒有發現任何明確不允許的代碼):OpenMP調用和僞指令允許在firstprivate變量構造?

#include <iostream> 
#include <vector> 
#include <omp.h> 

struct A { 
    int tid; 
    A() : tid(-1) { } 
    A(const A&) { tid = omp_get_thread_num(); } 
}; 

int main() { 
    A a; 

    std::vector<int> v(10); 
    std::vector<int>::iterator it; 
#pragma omp parallel for firstprivate(a) 
    for (it=v.begin(); it<v.end(); ++it) 
    *it += a.tid; 

    for (it=v.begin(); it<v.end(); ++it) 
    std::cout << *it << ' '; 
    std::cout << std::endl; 
    return 0; 
} 

我的動機是要弄清楚有多少線程,每個線程在OMP並行的 部分(我不希望把它用於正在處理雖然每個元素)的ID。有沒有可能導致未定義的行爲?

+0

代碼已損壞。 (1)'it!= v.end()',(2)'* it + = a.tid':這使得數據競爭。你到底想做什麼? – minjang

+0

準確的數據競賽是什麼?我遍歷矢量的所有值,它與使用索引和v.size()沒有區別 – ipapadop

回答

2

我僅解耦(開始)並行區域從環路,並使用專用變量,以保持TID:

std::vector<int>::iterator it; 
int tid; 
#pragma omp parallel private(tid) 
{ 
    tid = omp_get_thread_num(); 
    #pragma omp for 
    for (it=v.begin(); it<v.end(); ++it) 
     *it += tid; 
} 

補充:下面是來自the OpenMP specification(第2.9.3.4),讓我想起你的代碼是符合的報價,因此不會產生UB(但請參閱下面的另一個加):

...新的列表項目從構造之前存在的原始列表項目初始化。對於引用構造中任何語句中的列表項的每個任務,新列表項的初始化都會執行一次。初始化在構造執行之前完成。

有關一個paralleltask構建一個firstprivate條款,新列表項的初始值是存在緊接在遇到構建任務區域的構造,原列表項的值。

C/C++:...對於類類型的變量,調用複製構造函數來執行初始化。未指定調用類類型的不同變量的複製構造函數的順序。

C/C++:出現在firstprivate子句 中的類類型(或其數組)的變量需要類類型的可訪問的,明確的複製構造函數。

增加2:但是,沒有指定哪個線程執行firstprivate變量的複製構造函數。所以在理論上,可以通過變量的所有副本的區域主線程來完成。在這種情況下,omp_get_thread_num()的值將在所有副本中相等,或者在嵌套並行區域中爲外部區域中的線程號。 因此,從OpenMP角度來看,它可能會導致程序中的數據競爭。

+0

謝謝阿列克謝。這回答了我的問題。對象拷貝構造的順序並不重要。但是,他們沒有指定執行哪個線程是一個潛在的危險;儘管我不希望一個實現有一個線程出於性能原因實例化另一個線程的對象,但如果他們這樣做,我不會感到驚訝。 – ipapadop

0

當你遍歷矢量時,你應該使用它!= v.end(),而不是它< v.end()。但是,在這種情況下,您的並行for循環不再有效。我將重組代碼的該部分以下面的方式:

#pragma omp parallel for firstprivate(a) 
    for (int i = 0 ; i < v.size() ; i++) 
    v[i] += a.tid; 
+0

在OpenMP 3.0中允許這種類型的迭代(另請參見http://wikis.sun.com/pages/viewpage .action?PAGEID = 57508020)。 – ipapadop