平行計算 一 OpenMP

2021-09-24 09:19:08 字數 3912 閱讀 3591

openmp是一種用於共享記憶體並行系統的多執行緒庫,其支援c/c++、fortran,並且目前大多數常用編譯器,如vs內建編譯器、gcc、icc等都提供了openmp的相關支援,以gcc為例編譯時只需要新增-fopenmp選項即可完成openmp**的編譯。openmp中包含了一套編譯器偽指令、執行時函式和一些環境變數。其通過對序列**的很少的修改就可以實現序列**的並行化(不過要想得到更好效能依舊需要仔細的設計),並且可以自由控制編譯器是否忽略**中的並行段,從而實現一套**既可以序列亦可以並行,十分方便。

openmp提供的是多執行緒並行適用於多核cpu,不同執行緒間的資料可以直接共享,無需進行訊息傳遞。利用openmp將任務分為多個子塊,分發給不同的cpu核心從而提高多核cpu的利用率,加速程式的執行。

openmp測試就必須先說一下環境了,測試機器是雙核四執行緒intel的cpu,openmp預設使用四個執行緒進行並行。當然我們也可以通過在並行**塊前使用omp_set_num_threads(int)函式來手動指定使用的執行緒數量。

向量相加它又來了

#include

using

namespace std;

#define n 20000000

intmain()

這是乙個簡單的向量相加程式,這個程式的用時我之前的文章裡已經測過了,這裡不再測試,後面結果展示中這個序列程式的時間都是從前面文章裡拷貝的了。接下來我們用openmp並行它。使用openmp加速這種簡單迴圈很容易,只需要在for迴圈前面新增乙個#pragma omp parallel for即可。

#include

#include

"omp.h"

using

namespace std;

#define n 20000000

intmain()

測試一下速度,這裡就不測關閉優化時的效能了,gcc o3優化

未使用openmp

real	0m0.112s

user 0m0.040s

sys 0m0.072s

使用openmp

real	0m0.148s

user 0m0.095s

sys 0m0.251s

非常遺憾,openmp非但沒有提高效能反而使效能有所下降。接下來我們就不靠openmp自帶的迴圈優化來並行,而使用手動並行看一下效果。

想要手動並行需要使用的主要有#pragma omp parallel(用於告知編譯器接下來的**塊並行執行),omp_get_thread_num函式(獲得當前執行緒的執行緒號),omp_get_num_threads函式(獲得並行的匯流排程數)。

#include

#include

"omp.h"

using

namespace std;

#define n 20000000

intmain()

cout<<<

'\n'

;free

(x);

free

(y);

free

(z);

return1;

}

同樣使用o3優化用時為

real	0m0.065s

user 0m0.053s

sys 0m0.137s

可以看到這時的用時就少於序列的用時了,大概快了40%,為什麼4個執行緒沒有達到4倍的效能。這是因為機器本身是雙核的,四執行緒是通過一些其他技術多模擬出來了兩個執行緒,理論上四執行緒比雙線程的效能提公升並不能達到真四核四執行緒的效果。另外這個任務本身耗時就不多,而多執行緒是有額外開銷的,所以在一定程度上會導致四執行緒並行的效能達不到4倍。至於自動並行變慢原因,目前我也不是很明白,初步估計可能是自動並行的方式不對。

這是我以前踩過的乙個坑,這裡講一下。

序列**如下

#include

#include

using

namespace std;

#define n 80000000

intmain()

這個**主要是完成陣列的隨機初始化。接下來手動並行它。

#include

#include

#include

"omp.h"

using

namespace std;

#define n 80000000

intmain()

cout<<<

'\n'

;free

(x);

return1;

}

gcc o3優化,測試結果如下:

序列版本

real	0m0.935s

user 0m0.802s

sys 0m0.132s

並行版本

real	0m11.101s

user 0m14.880s

sys 0m23.758s

我們看到用時直接多了乙個數量級,我自己都覺得是不是測反了,但是沒錯確實並行版的效率更低了,這是為什麼呢?回答這個問題之前我們先自己做乙個偽隨機數生成器。

unsigned

rand

(int

&seed)

然後我們用自己的偽隨機數生成器再做一次並行的隨機數初始化

#include

#include

#include

"omp.h"

using

namespace std;

#define n 80000000

unsigned

myrand

(unsigned

&seed)

intmain()

#pragma omp barrier

//所有執行緒在此處同步後再向下執行

for(

int i=id*n/n;i<

(id+1)

*n/n;i++

) x[i]

=double

(myrand

(seed[id]))

/32768;}

cout<<<

'\n'

;free

(x);

free

(seed)

;return1;

}

這個程式用時是

real	0m0.194s

user 0m0.215s

sys 0m0.266s

這一下效能明顯提高了,而且達到之前效能的4倍。這又是為什麼呢?這就是前面說過的踩的坑,當初這個問題困擾了我近兩個月,使我一度懷疑openmp就是個廢物,根本沒有用。直到後來我了解了偽隨機數的生成演算法我才發現問題的關鍵。標準庫中的rand函式是使用乙個靜態變數來儲存隨機數種子的,每呼叫一次rand函式,函式就會修改一次這個種子,從而使得每一次生成的數字都不相同。那這和之前那個效率極低的並行程式又有什麼關係呢?正是因為每一次呼叫rand都要修改種子,所以rand執行時就既要讀一次種子又要寫一次種子,而不同執行緒又是共有乙個rand函式,也就是說共用乙個種子,那麼必然會出現不同執行緒同時讀寫種子的情況。這個時候為了避免讀寫衝突openmp會自動給種子加鎖,保證不同執行緒不會同時讀寫種子,這就導致了rand函式實際執行中並沒有並行,而是總在等待其他執行緒完成rand之後再去執行rand,程式依然還是序列的,而由於鎖的額外開銷就導致了並行版本的程式效能嚴重下降。當我們使用自己的隨機數生成器計算時,由於給每個執行緒各自分配了乙個種子,因此避免了不同執行緒共用乙個種子時產生的衝突,所以程式可以真正的並行執行,效能也提高了。

平行計算入門 openMP

openmp提供了對於並行描述的高層抽象,降低了並行程式設計的難度和複雜度,這樣程式設計師可以把更多的精力投入到並行演算法本身,而非其具體實現細節。對基於資料分集的多執行緒程式設計,openmp是乙個很好的選擇。同時,使用openmp也提供了更強的靈活性,可以較容易的適應不同的並行系統配置。執行緒粒...

OpenMP平行計算PI的值

用vs新建乙個工程後,首先要右鍵專案,屬性 c c 語言,在右邊的對話方塊中openmp支援選擇是 程式 為 include include include static long num steps 100000 double step,pi void main pi step sum clock...

OpenMP平行計算的使用5

openmp建立執行緒中鎖和原子操作效能比較 原子操作 在多程序 執行緒 的作業系統中不能被其它程序 執行緒 打斷的操作就叫原子操作。原子操作是不可分割的。include include include include define num 2000000 運算次數 using namespace ...