![](/img/trans.png)
[英]How to run unrolled 'for' loop (tmp) in parallel using openmp in c++?
[英]OpenMP and C++ parallel for loop: why does my code slow down when using OpenMP?
我有一個關於使用OpenMP(使用C ++)的簡單問題,我希望有人可以幫助我。 我在下面添加了一個小例子來說明我的問題。
#include<iostream>
#include<vector>
#include<ctime>
#include<omp.h>
using namespace std;
int main(){
srand(time(NULL));//Seed random number generator
vector<int>v;//Create vector to hold random numbers in interval [0,9]
vector<int>d(10,0);//Vector to hold counts of each integer initialized to 0
for(int i=0;i<1e9;++i)
v.push_back(rand()%10);//Push back random numbers [0,9]
clock_t c=clock();
#pragma omp parallel for
for(int i=0;i<v.size();++i)
d[v[i]]+=1;//Count number stored at v[i]
cout<<"Seconds: "<<(clock()-c)/CLOCKS_PER_SEC<<endl;
for(vector<int>::iterator i=d.begin();i!=d.end();++i)
cout<<*i<<endl;
return 0;
}
上面的代碼創建了一個向量v
,它包含[0,9]
范圍內的10億個隨機整數。 然后,代碼循環通過v
計算每個不同整數的實例數(即,在v中找到多少個,有多少兩個,等等)
每次遇到特定整數時,通過遞增向量d
的適當元素來計算它。 因此, d[0]
計算多少個零, d[6]
計算多少個六,依此類推。 到目前為止有道理嗎?
我的問題是當我嘗試使計數循環並行時。 如果沒有#pragma OpenMP
語句,我的代碼需要20秒,但是使用pragma
需要60秒。
很明顯,我誤解了一些與OpenMP相關的概念(也許是如何共享/訪問數據的?)。 有人可以解釋我的錯誤,或者指點我用一些有見識的文獻和適當的關鍵詞來幫我搜索?
你的代碼exibits:
競爭條件的出現是因為您在多個線程中同時更新向量d
的相同元素。 注釋掉srand()
行並使用相同數量的線程(但具有多個線程)多次運行代碼。 比較不同運行的輸出。
當兩個線程寫入彼此接近的內存位置以產生相同的高速緩存行時,就會發生錯誤共享。 這導致高速緩存行在多串口系統中不斷地從核心跳轉到核心或CPU到CPU,以及過多的高速緩存一致性消息。 每個高速緩存行有32個字節,向量的8個元素可以放在一個高速緩存行中。 每個高速緩存行有64個字節,整個向量d
適合一個高速緩存行。 這使得Core 2處理器上的代碼變慢,而Nehalem和后Nehalem(例如Sandy Bridge)上的代碼稍慢(但不像Core 2那么慢)。 真正的共享發生在兩個或多個線程同時訪問的元素上。 您應該將增量放在OpenMP atomic
構造中(慢),使用OpenMP鎖定數組來保護對d
元素的訪問(更快或更慢,取決於您的OpenMP運行時)或累積本地值,然后執行最終的同步減少(最快的)。 第一個實現如下:
#pragma omp parallel for
for(int i=0;i<v.size();++i)
#pragma omp atomic
d[v[i]]+=1;//Count number stored at v[i]
第二個實現如下:
omp_lock_t locks[10];
for (int i = 0; i < 10; i++)
omp_init_lock(&locks[i]);
#pragma omp parallel for
for(int i=0;i<v.size();++i)
{
int vv = v[i];
omp_set_lock(&locks[vv]);
d[vv]+=1;//Count number stored at v[i]
omp_unset_lock(&locks[vv]);
}
for (int i = 0; i < 10; i++)
omp_destroy_lock(&locks[i]);
(包括omp.h
以訪問omp_*
函數)
我讓你想出第三個選項的實現。
您正在使用clock()
測量已用時間,但它會測量CPU時間,而不是運行時間。 如果一個線程以100%的CPU使用率運行1秒鍾,則clock()
會指示CPU時間增加1秒。 如果您有8個線程以100%CPU使用率運行1秒鍾,則clock()
將指示CPU時間增加8秒(即8個線程乘以每個線程1個CPU秒)。 請改用omp_get_wtime()
或gettimeofday()
(或其他一些高分辨率計時器API)。
編輯一旦通過正確的同步解決了競爭條件,則適用以下段落,在此之前您的數據競爭條件不幸地使速度比較靜音:
您的程序正在變慢,因為在pragma部分中有10個可能的輸出隨機訪問。 沒有鎖定(您需要通過同步提供),OpenMP無法訪問任何這些元素,並且鎖定將導致您的線程具有比並行計數更高的開銷。
提高速度的一個解決方案是為每個OpenMP線程創建一個局部變量,該局部變量計算特定線程所見的所有0-10值。 然后在主計數向量中對它們求和。 由於線程不需要鎖定共享寫向量,因此這將很容易並行化並且更快。 我希望接近Nx加速,其中N是來自OpenMP的線程數,因為需要非常有限的鎖定。 此解決方案還避免了代碼中當前的許多競爭條件。
有關線程本地OpenMP的更多詳細信息,請參見http://software.intel.com/en-us/articles/use-thread-local-storage-to-reduce-synchronization/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.