OpenMPによる並列化 [Top]

目次
はじめに(謝辞)
OpenMPとは
OpenMP導入に関するメモ
古い情報

はじめに(謝辞)

本ページではSMP環境(共有メモリ)で並列化を実現できる共有仕様 OpenMPによる第一原理電子状態計算プログラムの並列化について話ていきたい と思います。
今回のOpenMPによる並列化は、基本的に本研究所の共通計算機導入過程にお けるベンチマークテストにおいて、SGI(日本シリコングフィックスクレイ ←当時)社によって提出された資料(その他に一部COMPAQ〔旧DEC〕 社〔←当時↓〕によるものも参考にしている)を元にしています。ここに、S GI及びCOMPAQ(旧DEC)の関係各位(営業、技術者の方々)に深く 感謝します。また、SGI社には本ウェブページへのベンチマークテスト結果 に関して快く掲載許諾してくれたことにも深く感謝する次第です。

OpenMPとは [先頭]

まず、OpenMPに関してのウェブページとして、www.openmp.orgサイトがあります。

そのものずばりという感じで、OpenMPに関しての詳細で豊富な情報が入手出 来ます。

動作環境

(SGI)
2009年4月から物質・材料研究機構のスーパーコンピュータが更新され、 新たにSGIのマシンが導入された。これを機に、筆者の持つバンド計算プロ グラムコードを本格的に並列化対応させることを決めた(並列化に関してスパ コン担当のSGIの方々の助言に深く感謝)。並列化は、ずっと以前に富士通の VPPで行なった経験があるが、今回のマシ ンにこれを適用すことは出来ない(VPP用の並列化指示注釈行は解釈出来な い)。今回は、OepnMPによる並列化を行なうことにした。OpenMPによる並列化 は、並列化を指示する注釈行を、FORTRANのソースコードの当該箇所に挿入す ることによって実現される。このため並列化出来ない環境下での流用も可能で あるし、デバッグもし易い(並列計算、非並列計算の比較がし易い)。
既にサブルーチン、FORCE, FORZFB, KBINT, DIAGON, CHAVER, MSDについて、 OepnMPによる並列化指示行を加え、並列化をほぼ完了させた。OpenMP化におい て、使用した並列化指示注釈行(例)は、


!$OMP THREADPRIVATE( /PSSNL/ )

(中略)

!$OMP PARALLEL DEFAULT(NONE)
!$OMP& COPYIN( SNL )
C!$OMP& FIRSTPRIVATE(  )
!$OMP& PRIVATE( CS,CP,CD, I1,I2, L1,L2,L3, MM, IIBA, IIKB,
!$OMP&  AKX,AKY,AKZ, LNUM,NNN, II, PPMT, 
!$OMP&  ZVN, ZZZ, ZAAA, PPM, IJG, EG,
!$OMP&  ZAWORK,WWW,ZWW,ICON, KBAS,KGB,IOPT,EPS )   
!$OMP& SHARED( KV3, WS,WP,WD, PAI4, GR, IG1,IG2,IG3, IGPO,
!$OMP&  VX,VY,VZ,IBA,IBA2,NBASE,NBMAT,NBD1,NBD2,
!$OMP&  ZCHGO, ZVXC, ZPSCC, ZFM3,  ZZ2,EKK,
!$OMP&  UNIVOL, KX1,KY1,KZ1, GX,GY,GZ, NLSPD )
!$OMP DO

(中略)並列化対象部分(k点に関するループ)

!$OMP END DO
!$OMP END PARALLEL
のようになる。FIRSTPRIVATEは、サブルーチンによって使う場合と使わない 場合がある。また上記以外に、CHAVERでは、!$OMP CRITICALと、!$OMP END CRITICALを使用する箇所があった。FORCEサブルーチンでは、 REDUCTION(+:ZFORC2)を使用。CRITICAL, REDUCTIONは、k点に関しての和を正 しく計算するために必要な並列化指示。
PARALLEL DEFAULT(NONE)にしておくと、並列化対象部分内の全ての変数を、 PRIVATEかSHAREDに定義しておく必要があるが、定義洩れを探すには便利(コ ンパイル時に未定義エラーが出る)である。DO 100 I=1,100(DOループ)にお ける変数、"I"などはDEFAULT(NONE)でもPRIVATEとして解釈されるので、書い てなくてもコンパイルは通る。
バンド計算においては、k点のループが最も独立性が高く、並列化もし易い ので、今回もk点に関するループで並列化を行なった。OpenMPでは、並列化に よってそれぞれ並列で動く部分をスレッドと呼んでいる。筆者が使用する環境 では、その各スレッドが各CPUに相当している。既に述べたように、並列化 したサブルーチンは、FORCE, FORZFB, KBINT, DIAGON, CHAVER, MSDである (4/27、2009現在)。いずれも、k点に関してのループがあり、それ が計算の大部分を占める。これは、VPPでも同様に並列化している。

今回(5/1、2009)OpenMPで並列化したサブルーチンの内容はそれぞれ、FORCE, FORZFB, KBINT, DIAGON, CHAVER, MSDとなります。
↑これらのプログラムソースは、無保証です(自己の責任において 参照、使用、利用して下さい)。筆者は、これらソースコードの使用、利用、 流用等によって生じる不利益、損害等に一切の責任を負いません。
"!$OMP"が、OpenMP並列化指示行。"C!$OMP"は、それに対する注釈(コメン ト)になっていて、これは並列化に関与しません。筆者による並列化は場当り 的な部分がまだあり、必ずしも並列化が効率的、効果的でない部分(特にメモ リに関して→データ分割が不十分、或いはデータ分割になっていない)があり ます。

OpenMP導入に関するメモ [先頭]

(↓古い情報↓) [先頭]

(COMPAQ)
COMPAQマシン上でのコンパイルは、オプションで-ompと指定すると OpenMP用の並列化指示行を加えたソースプログラムを解釈して並列実行させる ことが可能です。

動作環境は、本所COMPAQのalphaチップマシン(SMPマシン)です。 それは最新のFORTRAN90コンパイラが備わっており、それはOpenMPに対応して います(COMPAQ〔旧DEC〕のDigital FORTRAN90コンパイラでは、 ver5.1以降から、-ompオプションに対応しています。また、このバージョンの コンパイラが動作するためにはver4.0以上のDigital UNIX OSが必要です)。

このオプション-ompが使用できるのは(ここでは)f90(つまりFORTRAN90対 応コンパイラのみ)で、f77(FORTRAN77コンパイラ)ではOpenMPとして並列化 実行ファイルは作成できません。コードそのものは77仕様でも、それに OpenMP用並列化指示行を挿入してf90でコンパイルすることは可能で(f90は7 7仕様のコードも通す)、それによって生成された実行形式ファイルは、並列 で実行可能です。

(↓古い情報2↓〔SGI関連〕:今後大幅改訂予定)
ベンチマークテストにおける計算環境は、 SGIのOrigin2000システムで、CPUはMIPS R10000で、全プロセッサ 数は32です。

筆者のプログラムrevpe_d.fにOpenMPによる並列化指示行を加えたコードを どのようにコンパイル、実行させたかは、以下のmakefileの内容を参照して下 さい。

SHELL= /bin/csh
#
# makefile - Generated Jun 01, 1998 by fmaker V1.3 BMTOOLS (C)1992
#
BIN	= a.scs
MP	= -mp -mpio
FFLAGS	= -c -O3 -r8 -OPT:IEEE_arith=3:ro=3 
LFLAGS	= -w -O3 -r8 $(MP) -Wl,-Xlocal,work8_,-Xlocal,div_,-Xlocal,nonlc_,-Xlocal,recip_
CF77	= f77
LIB     = -lscs_mp -lfastm

SRC= \
apbo2.f   basnum.f  bccm.f    c2fft.f   c3fft.f   chaver.f  chgavr.f  chobsd.f \
clockm.f  conv2.f   dffctr.f  diagon.f  dlgama.f  dsj0.f    dsj1.f    dsjn.f \
dsjnv.f   energy.f  erf.f     evin.f    evou2.f   evout.f   ewvec.f   ewvmd.f \
fccm.f    fermi.f   force.f   forces.f  forloc.f  form.f    forzfb.f  gsfsca.f \
gstep1.f  gstepf.f  gstsca.f  hexm.f    hpsort.f  infout.f  input1.f  intchg.f \
kbint.f   kbmat.f   kpmsf.f   kpmwbz.f  kstep.f   latsca.f  lattic.f  main.f \
md.f      mffta4.f  mffta5.f  mffta6.f  mffta7.f  mffta8.f  mffta9.f  mfftb4.f \
mfftb5.f  mfftb6.f  mfftb7.f  mfftb8.f  mfftb9.f  mfftc4.f  mfftc5.f  mfftc6.f \
mfftc7.f  mfftc8.f  mfftc9.f  mfftdm.f  mfftds.f  mfftdv.f  mfftim.f  mfftis.f \
mfftiv.f  mfftom.f  mfftov.f  mfftp.f   mfftp1.f  mfftp2.f  mfftp3.f  mfftp4.f \
mfftrd.f  mfftri.f  mfftrp.f  mfftz0.f  msd.f     opgr.f    opmtrx.f  pcc.f \
pselmd.f  pseudo.f  r2fft.f   r3fft.f   sccm.f    simp.f    stress.f  strnl.f \
symm.f    symsca.f  tetrah.f  time.f    width2.f  xcfft.f   xstpc.f
 
OBJ= \
apbo2.o   basnum.o  bccm.o    c2fft.o   c3fft.o   chaver.o  chgavr.o  chobsd.o \
clockm.o  conv2.o   dffctr.o  diagon.o  dlgama.o  dsj0.o    dsj1.o    dsjn.o \
dsjnv.o   energy.o  erf.o     evin.o    evou2.o   evout.o   ewvec.o   ewvmd.o \
fccm.o    fermi.o   force.o   forces.o  forloc.o  form.o    forzfb.o  gsfsca.o \
gstep1.o  gstepf.o  gstsca.o  hexm.o    hpsort.o  infout.o  input1.o  intchg.o \
kbint.o   kbmat.o   kpmsf.o   kpmwbz.o  kstep.o   latsca.o  lattic.o  main.o \
md.o      mffta4.o  mffta5.o  mffta6.o  mffta7.o  mffta8.o  mffta9.o  mfftb4.o \
mfftb5.o  mfftb6.o  mfftb7.o  mfftb8.o  mfftb9.o  mfftc4.o  mfftc5.o  mfftc6.o \
mfftc7.o  mfftc8.o  mfftc9.o  mfftdm.o  mfftds.o  mfftdv.o  mfftim.o  mfftis.o \
mfftiv.o  mfftom.o  mfftov.o  mfftp.o   mfftp1.o  mfftp2.o  mfftp3.o  mfftp4.o \
mfftrd.o  mfftri.o  mfftrp.o  mfftz0.o  msd.o     opgr.o    opmtrx.o  pcc.o \
pselmd.o  pseudo.o  r2fft.o   r3fft.o   sccm.o    simp.o    stress.o  strnl.o \
symm.o    symsca.o  tetrah.o  time.o    width2.o  xcfft.o   xstpc.o

$(BIN): $(OBJ)
	f77 $(LFLAGS) -o $(BIN) $(OBJ) $(LIB)

.f.o:
	$(CF77) $(FFLAGS) $*.f
msd.o:	msd.f
	$(CF77) $(FFLAGS) $(MP) msd.f
diagon.o:	diagon.f
	$(CF77) $(FFLAGS)  $(MP) diagon.f
kbint.o:	kbint.f
	$(CF77) $(FFLAGS)  $(MP) kbint.f
kbmat.o:	kbmat.f
	$(CF77) $(FFLAGS)  $(MP) kbmat.f
force.o:	force.f
	$(CF77) $(FFLAGS)  $(MP) force.f
forzfb.o:	forzfb.f
	$(CF77) $(FFLAGS)  $(MP) forzfb.f
chaver.o:	chaver.f
	$(CF77) $(FFLAGS)  $(MP) chaver.f

clean:
	-rm -f core

clobber: clean
	-rm -f $(BIN) $(OBJ)

void:
	-rm -f makefile $(SRC)
残念ながら以上は、SGI社内におけるベンチマークテスト用のものであり、 筆者はSGI製のSMPマシンを利用できないので、これを実際に自分で検証 することはできていません。

(オプションの簡単な説明)
-r8:倍精度化オプション、REAL、COMPLEXをREAL*8、COMPLEX*16として扱う。
-mp,-mpio:並列化指示文(行)による並列化を指示
-Wl,-Xlocal,COMMON名:並列実行において指示したCOMMON変数をロー カル変数として扱う。
-lscs_mp -lfastm:科学計算ライブラリのリンク

並列化したプログラム

既に、全節で示してある通り、revpe_d.fが並列化の対象となっています。 これは筆者ウェブページで既に公開しているものとほぼ同じものです。既に、VPP 用に並列化が施されています。ベンチマークテストでは、SGI、COMPA Q(旧DEC)とも、このソースコードにOpenMP用の並列化指示行を挿入してい きました。

VPP(富士通のベクトル並列型スパコン)では、富士通による講習会、講 習会資料を元にほぼ自力でVPP用並列化コードを作成しました([参照1][参照2 ])。一方、SX(NECのベクトル並列型スパコン)では、原研との共 同研究により並列化の支援を受け、最初はrevpe_d.fコードの一部のルーチン を並列化してもらい、それを元にして、こちらで並列化を進めました(未完結、 [参照])。

今回は、先にも述べたようにベンチマークテストによって、SGI、COM PAQ側にOpenMPによる並列化コードを作成するという形になりました(ベン チマークの条件として、OpenMPによる並列化と、その実行テストを課した)。 従って、(3/3、1999段階)筆者はこれらOpenMPによって並列化された コードについてはほとんど手を付けていません。OpenMPに対しての知識もVP P、SXそれぞれ独自の並列化指示行と比べれば非常に貧弱です。これから筆 者もOpenMPによる並列化指示行を加えられたソースコード(revpe_d.f)を解 析しながら、勉強していくことになります(3/4、1999)。
(3/5、1999)、具体的なOpenMP化されたコードの部分を提示しなが ら、説明していきたいと思います。OpenMP化されたコードはSGIによるもの と、COMPAQ(旧DEC)によるものと2種類あるのですが、まず最初は、 当然すんなり動いたCOMPAQによるものから説明を行ないたいと思います。 SGIのコードは並列化以前に、Digital UNIX上のFORTRANコンパイラでは通 らない数値演算用のライブラリなどを使っていて、それらの問題に対処する必 要があるため後で詳しく話すことにします。
実際のOpenMPの並列化指示行によって並列化されたルーチンを示してみます。
以下は、サブルーチンDIAGON (ここのものとはバージョンは若干異なります)のOpenMP部分とその周辺を抜 粋したものです。現在使っている注釈行は、"!$OMP"となっている(5/ 1、2009)。

C     ////////////////////////////                                      
C     // DIAGONALIZE AT K-POINT //                                      
C     ////////////////////////////
C     VPP-PARALLEL START
C!XOCL PARALLEL REGION
!XOCL SPREAD NOBARRIER DO /IP

C$OMP PARALLEL SHARED(EKK,SSS,ZZ2,KZ1,KY1,KX1,UNIVOL,PAI4,NBMAT,
C$OMP& IBA2,IBA,NBASE,NBD2,NBD1,ZCHGO,ZPSCC,NLSPD,WD,WP,WS,ZVXC,
C$OMP& ZFM3,VZ,VY,VX,IGPO,IG3,IG2,IG1,GR,GZ,GY,GX)
C$OMP& PRIVATE(EPS,IOPT,KGB,KBAS,MM,I2,PPMT,II,L1,L2,L3,I1,LNUM,
C$OMP& CD,CP,CS,IIBA,IIKB,AKZ,AKY,AKX,IJG,PPM,EG,ZZZ,ZVN,ZAWORK,
C$OMP& ZWW,WWW,ZAAA,CWL,ICON)
C$OMP DO

      DO 100 NNN=1,KNV3                                                 
C                                  IWRT(NNN) =NNN                      
                                   AKX = VX(NNN)                        
                                   AKY = VY(NNN)                        
                                   AKZ = VZ(NNN)                        
      IIKB = IBA(NNN)                                                   
      IIBA = IBA2(NNN)                                                  
これをみると、OpenMPによる指示行は、C$OMPで始まり、それがOpenMPによ る指示行であるとコンパイラーが判定していることが推定されます。

C$OMP PARALLEL SHARED、C$OMP& PRIVATE(&記号は継続行を意味します)の 部分は、データ分割に関しての指示と思われます。 そして、これにはVPP用の並列化指示行も併記されているのですが、VP Pの場合と同じく、D0 100のループ(バンド計算上でのk点に関してのループ です)に並列化が施されています。そしてこれは、

  231 CONTINUE                                                          
  100 CONTINUE                                                          
C$OMP END DO
C$OMP END PARALLEL
!XOCL END SPREAD
C!XOCL END PARALLEL
      RETURN                                                            
      END                                                               
で閉じられています。

COMPAQ(旧DEC)マシンのソフトウェアに関してのマニュアルをお 持ちの場合、User Manual for DIGITAL UNIX Systemsの”DIGITAL Fortran 90”、 March 1998年版の6ー1、’Using Parallel Compiler Directives’を参照し て下さい。

OpenMPでも、これまで扱ってきたVPPやSXでの並列化指示行形式と同じ く、データに関しての分割と、手続きに関しての分割があるようです。この場 合の手続きの分割は既にあるVPP用の並列化指示と同じDOループに施され ています。データの分割は、一見するとかなり面倒なことをやっているように 見えます。
ただ、VPPでもSXでも、そしてこのOpenMPでも全て指示行形式なので、 指示行を解釈しないマシン上では注釈行として解釈されるます。
個々の指示行の分析

(3/26、1999)OpenMPで使用している指示行の説明を行なってみよ うと思います。

データ(配列など)分割部分

C$OMP PARALLEL SHARED(EKK,SSS,ZZ2,KZ1,KY1,KX1,UNIVOL,PAI4,NBMAT,
C$OMP& IBA2,IBA,NBASE,NBD2,NBD1,ZCHGO,ZPSCC,NLSPD,WD,WP,WS,ZVXC,
C$OMP& ZFM3,VZ,VY,VX,IGPO,IG3,IG2,IG1,GR,GZ,GY,GX)
C$OMP& PRIVATE(EPS,IOPT,KGB,KBAS,MM,I2,PPMT,II,L1,L2,L3,I1,LNUM,
C$OMP& CD,CP,CS,IIBA,IIKB,AKZ,AKY,AKX,IJG,PPM,EG,ZZZ,ZVN,ZAWORK,
C$OMP& ZWW,WWW,ZAAA,CWL,ICON)

手続き分割部分

C$OMP DO

C$OMP END DO
C$OMP END PARALLEL

OpenMP実行上の不具合修正(1/14、2000)

並列化している部分ではないですが、あるサブルーチンの局所配列で、それ をメインルーチンに引き渡しておく必要があったのにそれを怠っていました。 この場合、次のイタレーションで、その配列の値は本当は(サブルーチン内だ けの局所的なものなので)不定になっているはずなのですが、これまでの単一 のCPU動作では前のイタレーションの値は保持されていました(単なる偶然?)。 幸いこの局所配列の値そのものは保持されていても、されていなくても実害の ないものだったのですが、OpenMPによる並列計算では、実行時におそらくこの 配列に絡む計算のところでエラーが生じ、実行が停止していました。
当該配列変数を、メインルーチンへ正しく引き渡せるように書き直すことで、 このエラーによる実行停止はなくなりました(1/14、2000)。
(1/17、2000)正しく計算されていることを確認。尚、このバグの 詳細は、最近やらかした計算上のバグ、失敗のレポート1/17、2000を参照。
↑上記と同じ過ちを再び犯す(4/27、2009)。

(文章移動
(1/30、2009)今や、Core2duoやQuad core(Intel)など、1チップ 上に2ないし4個のCPUが載ったものが普通に存在し、Intel FORTRANコン パイラも、普通に並列計算に対応している。隔世の感あり。
(4/27、2009)筆者によるプログラムコードのOpenMP化を本格的に 始めている(開始は、4/7頃より)。既に、FORCE, FORZFB, KBINT, DIAGON, CHAVER, MSDについて、OpenMP化した。テスト計算段階で、速度は1 CPUの場合と比べて、8CPU並列実行で、およそ5.5倍の速度向上が実現 した。
(5/12、2009)データ分割に問題あり。
[先頭][総目次 ][最初に戻る][VPP並列化][V PP並列化2][SX4並列化][並列化情報]