簡體   English   中英

openmp中的並行for循環

[英]Parallel for loop in openmp

我正在嘗試並行化一個非常簡單的 for 循環,但這是我很長時間以來第一次嘗試使用 openMP。 我對運行時間感到困惑。 這是我的代碼:

#include <vector>
#include <algorithm>

using namespace std;

int main () 
{
    int n=400000,  m=1000;  
    double x=0,y=0;
    double s=0;
    vector< double > shifts(n,0);


    #pragma omp parallel for 
    for (int j=0; j<n; j++) {

        double r=0.0;
        for (int i=0; i < m; i++){

            double rand_g1 = cos(i/double(m));
            double rand_g2 = sin(i/double(m));     

            x += rand_g1;
            y += rand_g2;
            r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
        }
        shifts[j] = r / m;
    }

    cout << *std::max_element( shifts.begin(), shifts.end() ) << endl;
}

我編譯它

g++ -O3 testMP.cc -o testMP  -I /opt/boost_1_48_0/include

也就是說,沒有“-fopenmp”,我得到了這些時間:

real    0m18.417s
user    0m18.357s
sys     0m0.004s

當我使用“-fopenmp”時,

g++ -O3 -fopenmp testMP.cc -o testMP  -I /opt/boost_1_48_0/include

我得到了這些數字:

real    0m6.853s
user    0m52.007s
sys     0m0.008s

這對我來說沒有意義。 如何使用八核只能使性能提高 3 倍? 我是否正確編碼循環?

您應該對xy使用 OpenMP reduction條款:

#pragma omp parallel for reduction(+:x,y)
for (int j=0; j<n; j++) {

    double r=0.0;
    for (int i=0; i < m; i++){

        double rand_g1 = cos(i/double(m));
        double rand_g2 = sin(i/double(m));     

        x += rand_g1;
        y += rand_g2;
        r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
    }
    shifts[j] = r / m;
}

通過reduction每個線程在xy累積自己的部分和,最后將所有部分值相加以獲得最終值。

Serial version:
25.05s user 0.01s system 99% cpu 25.059 total
OpenMP version w/ OMP_NUM_THREADS=16:
24.76s user 0.02s system 1590% cpu 1.559 total

見 - 超線性加速:)

讓我們嘗試了解如何使用 OpenMP 並行化簡單的 for 循環

#pragma omp parallel
#pragma omp for
    for(i = 1; i < 13; i++)
    {
       c[i] = a[i] + b[i];
    }

假設我們有3可用線程,這就是會發生的事情

在此處輸入圖片說明

首先

  • 線程被分配了一組獨立的迭代

最后

  • 線程必須在工作共享構造結束時等待

因為這個問題被高度關注,所以我決定添加一點 OpenMP 背景來幫助那些訪問它的人


#pragma omp parallel使用一組threads創建一個並行區域,其中每個線程執行parallel region包含的整個代碼塊。 OpenMP 5.1可以閱讀更正式的描述:

當線程遇到並行構造時會創建一組線程來執行並行區域 (..)。 遇到並行構造的線程成為新組的主線程,在新並行區域的持續時間內線程編號為零。 新團隊中的所有線程,包括主線程,都執行該區域。 創建團隊后,團隊中的線程數在該並行區域的持續時間內保持不變。

#pragma omp parallel for創建一個parallel region (如前所述),並且該區域的threads將使用default chunk sizedefault schedule通常是static分配它所包含的循環迭代。 但是請記住, default schedule可能因OpenMP標准的不同具體實現而異。

OpenMP 5.1您可以閱讀更正式的描述:

工作共享循環結構指定一個或多個相關循環的迭代將由團隊中的線程在其隱式任務的上下文中並行執行。 迭代分布在執行工作共享循環區域綁定到的並行區域的團隊中已經存在的線程中

此外

並行循環結構是一種快捷方式,用於指定包含具有一個或多個關聯循環且沒有其他語句的循環結構的並行結構。

或者非正式地, #pragma omp parallel for是構造函數#pragma omp parallel#pragma omp for 在您的情況下,這意味着:

#pragma omp parallel for 
for (int j=0; j<n; j++) {

    double r=0.0;
    for (int i=0; i < m; i++){

        double rand_g1 = cos(i/double(m));
        double rand_g2 = sin(i/double(m));     

        x += rand_g1;
        y += rand_g2;
        r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
    }
    shifts[j] = r / m;
}

將創建一組線程,並將最外層循環的迭代塊分配給這些線程中的每一個。

為了使其更具說明性,使用4線程, #pragma omp parallel for chunk_size=1靜態schedule將導致類似的結果:

在此處輸入圖片說明

在代碼方面,循環將轉換為邏輯上類似於:

for(int i=omp_get_thread_num(); i < n; i+=omp_get_num_threads())
{  
    c[i]=a[i]+b[i];
}

其中omp_get_thread_num()

omp_get_thread_num 例程返回當前組內調用線程的線程號。

omp_get_num_threads()

返回當前團隊中的線程數。 在程序的順序部分 omp_get_num_threads 返回 1。

或者換句話說, for(int i = THREAD_ID; i < n; i += TOTAL_THREADS) THREAD_ID范圍從0TOTAL_THREADS - 1TOTAL_THREADS表示在並行區域上創建的團隊線程總數。

掌握了這些知識,並查看您的代碼,您可以看到變量“x”和“y”的更新存在競爭條件 這些變量在線程之間共享並在並行區域內更新,即:

     x += rand_g1;
     y += rand_g2;

要解決此競爭條件,您可以使用 OpenMP 的縮減條款:

指定每個線程私有的一個或多個變量是並行區域末尾的歸約操作的主題。

非正式地,reduce 子句將為每個線程創建變量 'x' 和 'y' 的私有副本,並在並行區域的末尾將所有這些 'x' 和 'y' 變量求和到原始變量中來自初始線程的“x”和“y”變量。

#pragma omp parallel for reduction(+:x,y)
for (int j=0; j<n; j++) {

    double r=0.0;
    for (int i=0; i < m; i++){

        double rand_g1 = cos(i/double(m));
        double rand_g2 = sin(i/double(m));     

        x += rand_g1;
        y += rand_g2;
        r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
    }
    shifts[j] = r / m;
}

您最多可以實現(!)是線性加速。 現在我不記得哪個是來自 linux 的時間,但我建議您使用 time.h 或(在 c++ 11 中)“chrono”並直接從程序中測量運行時間。 最好將整個代碼打包成一個循環,運行 10 次,平均得到 prog 的大約運行時間。

此外,您在 imo 上遇到了 x,y 問題 - 這不符合並行編程中數據局部性的范式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM