!$omp parallel CLAUSE
!$omp DIRECTION
[ structured block of code ]
!$omp end DIRECTION
!$omp end parallelhtml
其中DIRECTION是Openmp指令,有sections,do等,指定並行行爲。中間的linux
[ structured block of code ]express
便是須要併線的程序塊。除了上面的稱爲DIRECTION的指令語句外,Openmp還須要稱爲CLAUSE的從句對並行進行限制和說明。好比,須要對私有變量進行聲明時就須要用到private從句(這是常常要遇到的,後面會以例子說明)。在fortran的串行編譯下,以「!」打頭的都處於屏蔽狀態是不起做用的。加了openmp編譯參數後纔會生效。編程
我在程序編寫中用到最多的是do指令,偶爾用一下sections。do指令一般用來並行化do循環。原本用一個線程來執行的長的do循環被分割成幾個部分讓多個線程同時執行,這樣就節省了時間。sections指令一般用來將先後沒有依賴關係的程序塊(也即本來不分前後,你換下順序也無所謂)並行化。由於無關聯,因此能夠同時執行。windows
能夠說,若程序主要用來作計算,掌握了do和sections這兩個指令足矣! 數組
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I) !//Paralell塊開始
!$OMP SECTIONS !//Sections開始
!$OMP SECTION !//第一個section
DO I = 1, N/2
C(I) = A(I) + B(I)
END DO
!$OMP SECTION !//第二個section
DO I = 1+N/2, N
C(I) = A(I) + B(I)
END DO
!$OMP END SECTIONS NOWAIT !//Sections結束
!$OMP END PARALLEL !//Paralell塊結束app
這個並行語句將原本從1到N的循環手動分爲兩個部分並行執行。上面的shared,private就是從句(clause),聲明A,B,C爲公有的,而循環指標I是私有的。由於兩個section同時執行,都會對I進行改變,因此兩個section的循環指標必須彼此獨立,不能是同一個變量。PRIVATE會自動將這個會引起衝突的變量按需生成多個拷貝以供使用。最後的!$OMP END SECTIONS NOWAIT語句告訴兩個線程可各自自行結束,無需相互等待。函數
2.Do 指令的應用:
上面用Section實現的功能徹底能夠用Do來實現:spa
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I) !//Paralell塊開始
!$OMP DO !//Do的並行開始
DO I = 1, N
C(I) = A(I) + B(I)
END DO
!$OMP END DO !//Do的並行結束
!$OMP END PARALLEL !//Paralell塊結束線程
Do循環原本是從1到N,如今有多少個線程就分爲多少個部分執行,比上面的section更方便智能。不用擔憂循環次數N不能被線程數整除~。通常狀況下,各個線程均分循環次數,可是在某些循環指標下運算可能比較快,因此各個線程的運算時間可能不盡相同。這時候若是須要讓各個線程都結束了才能再往下(沒有NOWAIT),快的線程就必須等待慢的線程。爲了解決這個問題須要加上schedule從句,首行變爲以下:
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I),SCHEDULE(DYNAMIC)
這個SCHEDULE(DYNAMIC)從句告訴程序動態調整併線方式,那些任務輕鬆運算快的線程會自動去幫任務重運算慢的線程,力爭全部線程同時完成任務。
關於Do的積累計算,如累加,須要加上REDUCTION從句:
C=0.d0
!$OMP PARALLEL SHARED(A,C), PRIVATE(I),REDUCTION(+:C)
!$OMP DO
DO I = 1, N
C =C+ A(I)
END DO
!$OMP END DO
!$OMP END PARALLEL
這裏將累加分爲幾個部分由多個線程進行運算,因爲各個線程都在0.d0的基礎上開始計算它該算的部分,因此最後必須將各部分計算的結果再次求和。REDUCTION(+:C)從句就實現了這個效果。相似的疊乘等等用相似寫法,只需把「:」前的運算符改成乘法「*」便可。
2.據個人經驗,Fortran中若是不特別聲明,變量都是默認公有的。這一點能夠用DEFAULT(PRIVATE/SHARED)從句強行改變。循環指標默認是私有的,無需本身另外聲明。放在common域中的變量都是全局的,若要將這些全局變量私有化,可以使用threadprivate指令(參見文章:OpenMP並行編程:threadprivate指令)。
3.並行引導語句能夠簡化,但要注意先後配對。好比上面那個累加的例子能夠這樣寫:
C=0.d0
!$OMP PARALLEL DO SHARED(A,C), PRIVATE(I),REDUCTION(+:C)
DO I = 1, N
C =C+ A(I)
END DO
!$OMP END PARALLEL DO
也便可以將從句加在指令以後。
4.Fortran+Openmp的編譯問題:
通常來講,加上-openmp編譯參數便可。如:
ifort -openmp -o exe.out main.f
gfortran用-fopenmp編譯參數,g77和ifort同樣用-openmp參數。
若是用Makefile,將編譯參數放在合適的地方。
5.對於多重do循環,若是中間變量太多,對私有公有弄不清楚或者雖然清楚可是閒麻煩,能夠保留最外層循環,將裏面的循環在別處寫成一個子函數或子程序 ,而後在此處調用。這樣從結構上看就是對一重循環進行並行化,條理清楚不容易出錯。固然,傳遞給子函數或子程序的參數通常是要聲明私有的。
6.將串行程序改成Openmp並行程序後,在加與不加-openmp編譯參數的狀況下分別編譯並運算,比較並行與串行的結果,確保並行塊沒有改錯。
7.能夠在並行開始前指定由多少個線程來並行。在單cpu單核心的機器上也能夠(雖然沒有實際意義,但能夠用來調試並行程序):
CALL OMP_SET_NUM_THREADS(scalar_integer_expression)
其中scalar_integer_expression是個整形變量,指定並行的線程數目。
8.Openmp對私有變量的大小有限制。因此當遇到這樣的狀況,通常就是由這個限制形成的:不加openmp並行時程序沒有問題,加了openmp並行時出現斷錯誤(segmentation fault),可是當把某個(一些)私有數組的維數變小時,段錯誤消失並且和串行時結果一致。
解決辦法(linux下,windows下另外search)以下:
在linux終端執行
ulimit -s unlimited ;export KMP_STACKSIZE=2048000後一個數字參數足夠大便可。