OpenMP並行編程

  • 什麼是OpenMP?
    「OpenMP (Open Multi-Processing) is an application programming interface (API) that supports multi-platform shared memory multiprocessing programming in C, C++ and Fortran on many architectures, including Unix and Microsoft Windows platforms. It consists of a set of compiler directives, library routines, and environment variables that influence run-time behavior. 」
    簡單來講,OpenMP是一個能夠應用於多種平臺的共享內存式並行計算的接口。
  • Openmp的工做模式:
    Openmp的工做模式爲串行-並行-串行…。一開始的主線程是串行,當在須要並行的時候(這時候程序中應該有相應的Openmp指令語句),多個線程開始一塊兒工做。若當前的並行塊結束(仍舊由相應的Opemp指令語句來控制)時,又從新回到單一的主線程。如此可往復繼續。在一個四核心cpu上運行Openmp程序(並行塊的線程數默認是核心數目,這裏即爲4個線程),程序處於主線程時cpu利用率爲100%,可是當程序進入並行塊時全部的核心都會參與進來,cpu利用率會達到400%。若是程序的主要運算部分都處於並行區域,則絕大部分時間cpu都處於400%的工做狀態,這樣便大大提升cpu的利用率。
  • Openmp程序的結構:
    正如上面所說,編寫Openmp程序只須要在已有的串行程序上稍加修改便可:在並行開始和結束的地方加上Openmp語句引導並行的開始和結束。這些引導語句自己處於註釋語句的地位,必須在編譯時加上Openmp並行參數才能使其生效。若是不加編譯參數,編譯出來的程序仍舊是串行程序。
    Openmp是最容易實現的並行方式。
  • Openmp程序的編寫:
    下面以fortran語言爲例說明Openmp程序的編寫(對c語言和fortran語言,均可以參考本文最後給出的openmp教程)。通常的格式爲

     

    !$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這兩個指令足矣! 數組

  • 簡單的程序例子:
    1.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)從句就實現了這個效果。相似的疊乘等等用相似寫法,只需把「:」前的運算符改成乘法「*」便可。

  • 一些注意問題:
    1.尤爲要注意的問題就是變量的私有和公有問題。其實只要把握好一個原則,即若是這個變量有可能會被不一樣的線程同時進行寫操做(這不是你但願看到的),則這個變量就應該聲明爲私有。通常來講,並行體中臨時用到的一些中間變量應該是私有的。

     

    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後一個數字參數足夠大便可。

相關文章
相關標籤/搜索