global_result=
0.0;
#pragma omp parallel num_threads(thread_count)
openmp提供了乙個更清晰的方法來避免local_trap的序列執行:將global_result定義為乙個歸約變數。歸約操作符是乙個二元操作,規約就是將相同的歸約操作符重複地應用到運算元序列來得到乙個結果的計算。另外,所有操作的中間結果儲存在同乙個變數中:歸約變數。
global_result=
0.0;
#pragma omp parallel num_threads(thread_count)\\預處理指令一行寫不完就加乙個\
reduction(+: global_result)
gloabl_result+
=local_trap
(a,b,n);/*
這麼來理解
把上面的程式寫成
global_result=0.0;
#pragma omp parallel num_threads(thread_count)\\預處理指令一行寫不完就加乙個\
*/
**明確了global_result是乙個歸約變數,加號(+)指示歸約操作符是加法。openmp為每個執行緒有效地建立了乙個私有變數,執行時系統在這個私有變數中儲存每個執行緒的結果。openmp也建立了乙個臨界區,並且在臨界區中,將儲存在私有變數中的值進行相加。
reduction子句的語法
reduction(:
)
在c語言中,操作符可能是+,*,-,&,|,^,||中的任意乙個,但是減法可能會有問題,因為減法不支援交換律和結合律。
注意,如果歸約變數是float和double,當使用不同數量的執行緒時,結果可能會有些不同。這是因為浮點數不滿足結合律。
當乙個變數被包含在乙個reduction子句中時,變數本身是共享的。然而執行緒組中的每個執行緒都建立自己的私有變數。在parallel塊裡,每當乙個執行緒執行涉及這個變數的語句時,它使用的其實是私有變數。當parallel塊結束後,私有變數中的值被整合到乙個共享變數中。
最後注意,reduction子句建立的私有變數初始化為相同的值。
h=
(b-a)
/n;(
f(a)+f
(b))
/2.0
;#pragma omp parallel for num_threads(thread_count)\
for(i=
1;i<=n-
1;i++)=
f(a+i*h)
;
for指令的警告
首先openmp只能並行化for迴圈,只能並行化那些可以在如下情況下確定迭代次數的for迴圈:
由for語句本身(for(…;…;…))來確定
在執行之前確定。
例如
for(;
;)//無限迴圈不可以並行化
for(i=
0;i)//不能被並行化,因為迭代次數不能只從for語句來確定。這個for迴圈也不是乙個結構化塊,因為break新增了另乙個從迴圈退出的出口
執行時系統必須能夠在執行前確定迭代的數量,但是唯一例外的是:在迴圈體中可以有乙個exit呼叫
在迴圈中,迭代中的計算依賴於乙個或多個先前的迭代結果。這樣使用for指令的結果是不可預計的。
兩個要點
(1)openmp編譯器不檢查被parallel for指令並行化的迴圈所包含的迭代間的依賴關係,而是由程式設計師來識別這些依賴
(2) 乙個或多個迭代結果依賴於其它迭代的迴圈,一般不能被openmp正確地並行化。
f[i]=f[i-1]+f[i-2],此時f之間的依賴關係稱為資料依賴或迴圈依賴
尋找迴圈依賴
當我們試圖使用parallel for指令時,首先就要注意迴圈依賴。
重點關注在乙個迭代中被讀或被寫,而在另乙個迭代中被寫的變數
在序列**
double factor=
1.0;
double sum=
0.0;
for(k=
0;k)4.0
*sum;
直觀的openmp版本
double factor=
1.0;
double sum=
0.0;
#pragma omp parallel for num_threads(thread_count) \
reduction(+:sum)
for(k=
0;k)4.0
*sum;
發現上面的第七行和第八行存在迴圈依賴。
改進的openmp
double factor=
1.0;
double sum=
0.0;
#pragma omp parallel for num_threads(thread_count) \
reduction(+:sum)
for(k=
0;k)4.0
*sum;
double sum=
0.0;
#pragma omp parallel for num_threads(thread_count) \
reduction(+:sum) private(factor)
for(k=
0;k)4.0
*sum;
在parallel子句內列舉的變數,在每個執行緒上都有乙個私有副本被建立。因此乙個執行緒對factor的更新不會影響另乙個執行緒的factor值。
要更記住的是,乙個私有作用域的變數的值在parallel塊或者parallel for塊的開始處是未指定的。他的值在parallel或者parallel for塊完成之後也是未指定的。
int x=5;
//這裡的x跟下面的x不是一回事
#pragma omp parallel num_threads(thread_count) private(x)
printf
("after parallel block,x=%d\n"
,x);
//這裡的printf也是非確定的,因為他在parallel塊完成之後列印x
openmp提供了乙個子句default,該子句顯式地要求我們這樣做。如果我們1新增子句
default
(none)
到parallel或parallel for指令中,那麼編譯器將要求我們明確在這個塊中使用的每個變數和已經在塊之外宣告的變數的作用域。(在乙個塊中宣告的變數都是私有的,因為他們會被分配給執行緒的棧)
例如使用default子句,對pi的計算如下
double sum=
0.0;
#pragma omp parallel for num_threads(thread_count) \
default(none) reduction(+:sum) private(k,factor) shared(n)
for(k=
0;k)4.0
*sum;
在這個例子中,for迴圈中使用4個變數。由於default子句,我們需要明確每個變數的作用域。
sum是乙個歸約變數,同時擁有私有和共享作用域的屬性。
factor和k應該擁有私有作用域
從未在parallel塊中更行的變數n,應該被安全地共享。共享變數在塊內具有parallel塊之前同樣的值,在塊之後的值與塊內的最後乙個值相同。
《並行程式設計導論》03openmp
氣泡排序 for list length n list length 2 list length 這裡陣列a儲存n個int型整數,演算法將他們公升序排列。外部迴圈先找到列表最大元素將他存在a n 1 中,然後尋找次大的元素並存在a n 2 中,以此類推。因此,第一遍處理全部的n個元素,第二遍處理除了...
《並行程式設計導論》04openmp
parallel for指令中,將各次迴圈分配給執行緒的操作是由系統完成的。然而,大部分openmp實現只是粗略地使用塊分割。乙個更好的分配方案是輪流分配執行緒的工作 迴圈劃分 在迴圈劃分中,歌詞迭代 流地一次乙個地分配給執行緒。不難發現,乙個好的迭代分配能夠對效能有很大的影響。在openmp中,將...
OpenMP並行程式設計(一)
openmp並行程式設計 一 openmp是乙個支援共享儲存並行設計的庫,特別適宜多核cpu上的並行程式設計。今天在雙核cpu機器上試了一下openmp並行程式設計,發現效率方面超出想象,因此寫出來分享給大家。在vc8.0中專案的屬性對話方塊中,左邊框裡的 配置屬性 下的 c c 下的 語言 頁裡,...