J. Plasma Fusion Res. Vol.89, No.1 (2013) 49‐56

講座 核融合プラズマシミュレーションの技法−大規模並列計算環境の活用−

２．並列コードを開発するための基礎技法
坂上仁志
核融合科学研究所（原稿受付：２
０
１
２年１
１月１
２日）

本章では，まず，並列計算機のアーキテクチャ（内部構造）について簡単に紹介し，並列実行できるコードをプログラミングするために最低限必要な事柄を概説し，特に注意を払わなければならないプロセス間通信について述べる．そして，最後に並列コードを開発するための環境について，最新の状況を紹介する．
Keywords:
code development, parallelization, parallel language

２．
１ はじめにティ・コンピュータを安価なネットワークで結合した，いコンピュータを用いたシミュレーションは，理論，実験

わゆる Beowulf 型の PC クラスタ型並列計算機でも同様でに次ぐ第三の重要な研究手段であり，コンピュータの性能ある．つまり，分散されたメモリを前提とした並列計算機向上に伴って，研究は，より詳細に，より大規模になってと並列プログラミングが，これからの高性能コンピューきた．しかし，コンピュータを構成する基本要素であるプ

ティングには必須である．

ロセッサの性能向上は，命令レベル並列性の壁，消費電力

２．
２ 並列計算機のアーキテクチャの壁，メモリの壁という，三つの壁に突きあたっており，さらなる高性能を実現することは困難と考えられている

コンピュータにおいて一連の演算を行うハードウェア［１］．まず，命令レベル並列性の壁とは，１クロックあたり

は，従来 CPU と呼ばれることが多かったが，ここではコアの演算性能向上をめざして，同時に複数の命令が並列実行と呼ぶことにする．コアはメモリと接続されており，メモできるハードウェアをプロセッサに導入したが，命令やリからデータを読み出して演算し，その結果をメモリに書データに依存関係があるため，実際に並列実行できる場合

き込むことが基本動作である．また，コアが，外部のハーは非常に限定的であり，そのハードウェアを有効に活用でドウェアにアクセスできるようにするためのコントローラ

きなかったことである．その一方で，プロセッサの消費電がある．このひとまとまりを計算ノードと呼ぶことにし，力は，熱密度の観点から限界近くに達しており，電圧を上

図１（a）に示す．

げることで動作周波数を高くし，性能向上を図るという手

まず，以下のプログラムを考える．

法は通用しなくなっている．これを消費電力の壁と呼ぶ．

do_i_=_1,_N

最後のメモリの壁とは，メモリのアクセス速度の向上はプ___a(i)_=_b(i)_+_c(i)ロセッサの演算性能の向上よりずっと遅く，両者の乖離がend_do著しくなっており，キャッシュだけでは，もはや，その溝

do_i_=_1,_N

を埋めることができないということである．そして，命令___a(i)_=_s_*_b(i)_+_t_*_c(i)_*_c(i)レベル並列性の壁は，必然的にスレッドレベルやプロセス

end_doレベルの並列性が重要であることを示唆している．また，一般に消費電力は，動作周波数のおおよそ自乗に比例する

最初のループでは，演算一回毎に，データの読み出しがため，マルチコア化によりコア数を２倍にし，動作周波数

２つ，書き込みが１つ必要である．ここで，簡単のため次を 1/2 にすれば，同一の演算性能で消費電力は半分になる．

のデータを読み出しているときに，今計算したデータを同

このため，動作周波数を下げてマルチコア化することは，時に書き込めるとする．例えば，コアの演算性能が 100

消費電力の壁に対する一つの解決策であるが，マルチコア

MFLOPS であるとすると，１秒間に１億回の演算ができを効率良く使う並列プログラムの存在が前提である．最後

ることになり，この性能を活かすためには，１億分の一秒のメモリの壁も，ローカライズされたメモリ上のデータとごとに２つのデータをメモリからコアに 転 送 しなければならコアの組み合わせが重要となることを示しており，分散メ

ない．単精度の実数変数は４バイトであるので，コアとメモリ型の並列計算が必要となる．この流れは，コモディ

モリ間のデータ転送速度（メモリバンド幅）は，800 MB/s

2. Elementary Technique to Develop Parallelized Codes
SAKAGAMI Hitoshi

author’s e-mail: sakagami.hiroshi@nifs.ac.jp

４９!2013 The Japan Society of Plasma
Science and Nuclear Fusion Research

Journal of Plasma and Fusion Research Vol.89, No.1 January 2013

モジュールをモジュール間の通信機構とともに１つの計算ノードとして実装している高性能なコンピュータもあり，
これを図１（c）に示す．このアーキテクチャは，計算ノード内において，一見分散メモリ型の並列計算機を構成しているようであるが，すべてのコア間でモジュールを跨ってメ
モリを共有しており，より多くのコアを使った共有メモリ
型の並列処理が可能である．しかし，メモリアクセス速度が均等ではないことに注意しなければならない．即ち，コ（a）
単一コアの計算ノード

アと同じモジュール内のメモリには，メモリバンド幅でア（b）マルチコアの計算ノード

クセスできるが，他モジュールのメモリには，一般には，それより更に遅いモジュール間の通信機構を介しなければならず，このようなメモリ構成は，NUMA（NonUniform
Memory Access）と呼ばれる．NUMA では，モジュール間通信機構の性能が足枷となり，モジュール内のコア数以上のコアを用いて共有メモリ型並列処理を行っても，性能は向上しない可能性がある．また，モジュール内のコア数以下のコアを用いる場合でも，あるモジュール内だけのコアを使う場合と他モジュール内のコアも一緒に使う場合には，性能差が現れる．さらに，演算するコアを含んだモジュールとは別のモジュール内のメモリを使うこともできるため，同じモジュール内のメモリを使う場合とは，異なった性能を示す．このように，複数のモジュールから成（c）複数モジュールから構成される計算ノード

る計算ノードを使う場合は，どのモジュールのコアとメモ

図１：計算ノードのアーキテクチャ．

リを使うかを制御することが重要となり，一般にはアフィニティ制御と呼ばれる．例えば，Linux では，numactl コマ

必要になる．後のループでは，同じデータ量について，演

ンドで制御できるので，参照されたい．

算回数が４回なので，メモリバンド幅は 200 MB/s あれば，

さて，並列計算機では上記で述べた計算ノードを通信十分である．このメモリバンド幅と演算性能の比は B/F

ネットワークで接続し，計算ノード間でデータ転送できると呼ばれ，コアの実効性能を知る上で非常に重要な指標でようにしている．高性能な並列計算機では，その通信ネッ

ある．B/F が２のコンピュータでは，後のループは１００％のトワークに高速な専用ハードウェアを用いていることが多性能で実行されるが，最初のループは２５％の性能しか発揮

く，PC クラスタ型並列計算機では，Giga Bit Ethernet 等のできないことになり，むやみに演算性能だけを向上させてLAN を構築するためのハードウェアを用いることが多も意味はなく，これが，メモリの壁の正体である．コモい．複数ノードを用いて並列処理をする場合，この計算

ディティ・コンピュータでは，B/Fが0.1以下であることもノード間の通信性能が，並列処理の効率に大きな影響を与珍しいことではなく，カタログに書かれている性能を実感える．このため，計算ノードの演算性能とノード間の通信できない主因である．しかし，メモリバンド幅を向上させ速度のバランスを考えることが重要となる．

るためには，非常に大きなコストが必要なため，大きな２．
３ 並列コードのプログラミング

B/F の実現は現実的には難しい．そこで，通常，コアにはキャッシュと呼ばれる内部機構が付加されている．キャッ性能のいい並列コードを作成するためには，並列計算機

シュとメモリ間のデータ転送速度は，演算速度に比較してのアーキテクチャを考慮したプログラミングが必要であ

遅いメモリバンド幅であるが，コアとキャッシュ間は高速

る．まず，前節で述べたキャッシュをうまく活用するため，にデータ転送ができる．このため，演算に必要なすべての配列変数へのアクセスは，できるだけ連続アクセスになる

データを一旦キャッシュに格納できると，メモリにアクセ

ように，配列変数とループを定義する．Fortran におけるスすることなく演算ループが実行できるので，プログラム

多次元配列では，より左側の添え字が優先されて小大順にのその部分だけはメモリバンド幅に縛られないで，コアの変化するように配列要素がメモリ上に並ぶ．なお，C 言語

演算性能を発揮できることになる．

では，逆により右側の添え字が優先されて小大順に変化す

最近のマルチコア化の流れにより，計算ノードは，コモるので注意すること．さて，ループ A および B を含む以下

ディティ・コンピュータであっても，複数のコアが一体とのプログラムを考えると，配列変数について，ループ A

なってメモリに接続されている場合が多く，これを図１

では 10,000 要素毎の飛 び 飛 びのアクセスになっているが，（b）に示す．この場合，それぞれのコア間でメモリを共有

ループ B では連続アクセスになっている．この２つのルーしており，いわゆる共有メモリ型の並列処理が可能であ

プでは，演算やメモリアクセスの総量は同じであるが，実る．さらに，この計算ノードをモジュールとして，複数の行速度に大きな差があり，当然，後者の方がずっと速い．
５０Lecture Note

2. Elementary Technique to Develop Parallelized Codes

parameter(N=10000)!_x(i)=pv(1,i),y(i)=pv(3,i),dimension_a(N,N),_b(N,N)!_vx(i)=pv(2,i),vy(i)=pv(4,i)!_Loop_A

do_i_=_1,_N

do_i_=_1,_N___pv(1,i)_=_pv(1,i)_+_pv(2,i)_do_ii_=_1,_N___pv(3,i)_=_pv(3,i)_+_pv(4,i)____a(i,ii)_=_a(i,ii)_+_b(i,ii)

end_do_end_doH. Sakagami

上記の書き換えでは，配列変数pvの１次元目添え字と物

end_do理量の関係が不明瞭なため，プログラムの意味が理解しに!_Loop_B

くい．そこで，構造体を使って以下のように書くこともで

do_ii_=_1,_N

きる．

_do_i_=_1,_N____a(i,ii)_=_a(i,ii)_+_b(i,ii)

parameter(_N=10000_)_end_dotype_particle

end_do___sequence___real_::_x,_vx,_y,_vy

キャッシュ容量は，メモリ量に比べると非常に小さいた

end_type

め，通常は配列変数の全要素をキャッシュに格納すること

type(particle)_::_pv(N)はできない．この場合，連続アクセスを実現しただけでは，

do_i_=_1,_N

キャッシュを有効に活用できずに性能低下を招く．シミュ___pv(i)%x_=_pv(i)%x_+_pv(i)%vx

レーションに現れる典型的なループとして以下のプログラ___pv(i)%y_=_pv(i)%y_+_pv(i)%vy

ムを考える．

end_doparameter(_N=10000_)

さらに，書き換えなければならない箇所が多い場合，面

real_x(N),_y(N),_vx(N),_vy(N)倒な作業になり，かつ修正ミスを誘発してバグが混入しや

do_i_=_1,_N

すい．この場合，以下のようにコンパイラのプリプロセッ___x(i)_=_x(i)_+_vx(i)サ機能を使うと簡略化できるので，参考にされたい．

___y(i)_=_y(i)_+_vy(i)end_doparameter(_N=10000_)#define_x(i)_pv(i)%x

まず，x(i)と vx(i)がアクセスされるので，それぞれの#define_y(i)_pv(i)%y

メモリ上のアドレスを先頭とする連続したデータブロック#define_vx(i)_pv(i)%vx

がキャッシュに一括ロードされ，ループ内の１行目が計算#define_vy(i)_pv(i)%vy

される．プログラムの実行順序に従 う と，次 に y(i)とtype_particle

vy(i)がアクセスされるが，これらはキャッシュには格納___sequence

されていない．この状態をキャッシュミスと呼ぶ．このた___real_::_x,_vx,_y,_vy

め，キャッシュ容量が少ない場合には，先程ロードした

end_typex(i)と vx(i)の後続データを破棄し，新たにメモリから

type(particle)_::_pv(N)y(i)と vy(i)およびそれらの後続データがキャッシュ上

do_i_=_1,_Nに一括ロードされ，ループ内の２行目が計算される．その___x(i)_=_x(i)_+_vx(i)次にアクセスされるのは，x(i+1)と vx(i+1)であり，同___y(i)_=_y(i)_+_vy(i)様にキャッシュミスが起こり，メモリから再びデータを

end_doロ ー ド す る．こ の よ う に，キ ャ ッ シ ュ ミ ス が 頻 発 し，
キャッシュに前もってロードされたデータを有効にアクセ

さて，これでようやくコードの並列プログラミングの準スできない場合には，演算性能が大幅に低下する．そこで，備ができたことになる．まず試すべきは，コンパイラの自以下のようにプログラムを書き換えて，計算に必要なデー

動スレッド並列化機能の活用であろう．プログラムを修正

タが連続アドレスに配置されるよう最適化する．この場することなく必要に応じてコンパイラ固有の指示行を挿入

合，最初のアクセスで pv(1,i)と後続のデータがキャッするだけで，コードの共有メモリ型のスレッド並列化がで

シュ上に一括ロードされると，それらのデータをすべて使きる．一般に，並列化された一つのスレッドは，メモリをい切るまで，キャッシュミスが発生しないため，キャッ共有する一つのコア上で実行される．この方法は，非常にシュを有効に活用できる．

お手軽であるが，期待した性能を得られない場合も多い．
また，良好な性能が得られている場合でも，それはコンパ

parameter(_N=10000_)

イラの能力に依存していることには注意されたい．コード

real_pv(4,N)の並列性を解析する能力はコンパイラによって異なるた
５１

Journal of Plasma and Fusion Research Vol.89, No.1 January 2013

め，同じコードを別のコンパイラでコンパイルすると，

do_i_=_1,_n

まったく自動並列化が働かないことも考えられる．また，___aipd_=_aipd_+_a(i)_*_b(i)このような指示行は一般にコンパイラ固有であり，別のコ

end_doンパイラでは無視されるため，新しいコンパイラ用の指示

write(*,*)_’aipd_=_’,_aipd

行を再度挿入する羽目になってしまう．このため，ほんのstop

僅かな手間で自動並列化できない場合は，そのコンパイラ

endの指示行を用いて並列プログラミングをするのではなく，OpenMP［２］を使うべきである．

これを MPI で並列プログラミングし，内積計算を並列化

OpenMP は，従来のプログラムに指示行を挿入するだけするためには，以下のように書かなければならない．

で共有メモリ型のスレッド並列を記述できる標準化された
parameter(n=100)

インターフェイスであり，ほとんどのコンパイラで利用す

real_a(n),_b(n)ることができる．上述のループでは，以下のように書くだ

integer_istat(MPI_STATUS_SIZE)

けで並列化されるが，自動並列化とは異なり，指示行で並

call_MPI_INIT_(_ierr_)

列実行を指定しないループは，一切並列化されない．なお，

call_MPI_COMM_SIZE_(_MPI_COMM_WORLD,_np,_ierr_)

一つのスレッドを一つのコア上で実行することは，自動ス

call_MPI_COMM_RANK_(_MPI_COMM_WORLD,_id,_ierr_)

レッド並列化と同様である．

if(_id_.eq._0_)_then!$OMP_PARALLEL_DO__read(*,*)_a,_b__do_i_=_1,_N__do_i_=_1,_np-1___x(i)_=_x(i)_+_vx(i)is_=_(_n_/_np_)_*_i_+_1___y(i)_=_y(i)_+_vy(i)call_MPI_SEND_(_a(is),_n/np,_MPI_REAL,_i,_&

end_do___1,_MPI_COMM_WORLD,_ierr_)
call_MPI_SEND_(_b(is),_n/np,_MPI_REAL,_i,_&

OpenMP を用いるとコンパイラ環境に依存しないで，比___1,_MPI_COMM_WORLD,_ierr_)

較的容易に並列プログラミングできるが，複数のコア（＝__end_doスレッド）から一斉に，近接したアドレスのメモリにアクelse

セスが集中するので，メモリバンド幅の制限が大きく影響__call_MPI_RECV_(_a,_n,_MPI_REAL,_0,_1,_&

し，たとえキャッシュがあったとしても，コア数が多くな__________________MPI_COMM_WORLD,_istat,_ierr_)

ると並列処理による性能向上は得にくくなる．このため，__call_MPI_RECV_(_b,_n,_MPI_REAL,_0,_1,_&

数十コア以上を用いて良好な並列性能を得るためには，__________________MPI_COMM_WORLD,_istat,_ierr_)

MPI（Message Passing Interface）［３］と呼ばれるプログラ

end_if

ミングインターフェイスを用いて並列プログラミングし，

aipdt_=_0.0

複数の計算ノードから構成される分散メモリ型の並列計算

do_i_=_1,_n_/_np

機上で実行しなければならない．

__aipdt_=_aipdt_+_a(i)_*_b(i)MPI とは，分散メモリ型の並列計算をサポートするため

end_doの標準化されたライブラリ群であり，自分のコードからサ

call_MPI_REDUCE_(_aipdt,_aipd,_1,MPI_REAL,_&

ブルーチンを呼び出す形で利用する．MPI では，計算を行______________MPI_SUM,0,MPI_COMM_WORLD,ierr)

う単位をプロセスと呼び，各プロセスはそれぞれ独立した

if(_id_.eq._0_)_write(*,*)_’aipd_=_’,_aipd

固有のメモリ空間を持つ．並列計算に必要なプロセス間の

call_MPI_FINALIZE_(_ierr_)

データ転送は，プログラム実行のフローを意識した上で，stop

ユーザが明示的に記述しなければならず，デッドロックのend

危険が常にある．この場合も，一般には，並列化された一つのプロセスは，一つのコア上でメモリを共有することな

ここでは，np 個のプロセス（各プロセスは id=0, 1,

く実行される．なお，計算ノードのアーキテクチャを考慮..., np-1 の番号で識別される）で並列処理を行っていし，ハイブリッド並列と呼ばれる複数のコア上で一つのプる．まず，id=0 のプロセスがデータを配列 a，b に読み込

ロセスを実行する場合もあるが，これについては次節で述

み，配列 a，b の一部分のデータを id＝1, 2, ..., np-

べる．さて，例えば，２つのベクトルを読み込んで内積を1 のプロセスに送信（MPI_SEND）している．一方，id=求める以下のプログラムを考える．

1, 2, ..., np-1 のプロセスは，id=0 のプロセスからのデータを受信（MPI_RECV）する．そして，各プロセスは，

parameter(n=100)

自身が担当する部分の和 aipdt を計算し，最後にリダク

real_a(n),_b(n)ション処理（MPI_REDUCE）により全プロセスと通信／演

read(*,*)_a,_b

算し，全体和である内積 aipd を求めている．このように，

aipd_=_0.0

MPI を用いた並列プログラミングは，計算機科学（Com５２

Lecture Note

2. Elementary Technique to Develop Parallelized Codes

H. Sakagami

puter Science）の研究者ではない計算物理（Computational

セスのデータだけで計算できるように，データの配置等を

Physics）の研究者や一般ユーザには，やや難しいものと工夫することであり，場合によってはアルゴリズムを変えなっている．しかし，MPI は，ほとんどすべての並列計算ることも検討する．例えば，FFT はスペクトル法でよく使機上に実装されており，デファクト標準である．

われているが，FFT そのものは全プロセスのデータが必要

なお，MPI を用いたプログラムは，共有メモリ型の並列であり，通信負荷は大きい．このため，通信負荷が小さい

計算機上でも実行することが可能であり，各プロセスはメ

FFT を使わないアルゴリズムが有効な場合もある．例え

モリ空間上には独立に割り付けられるため，近接したアド

ば，１００台の並列計算では，通信負荷が大きいため１０倍しかレスのメモリにアクセスが集中しない．このため，共有メ

速くならないアルゴリズムより，一台で計算する場合は５モリ型並列計算機であっても，スレッド並列のプログラム倍遅くても，通信負荷が小さくて１００台で８０倍速くなるア

より，プロセス並列の MPI プログラムの方が高速に並列実

ルゴリズムの方が，前者より最終的には 1.6 倍速く計算で行できる場合がある．

きる．また，全データにおける総和や最大値等を求めるい

最後にスーパースカラについて述べる．問題サイズを固わゆるリダクション処理も可能な限り少なくすることがポ定して並列計算を考える場合，並列処理するプロセス数がイントである．リダクション処理における通信量は一般に増える程，一般に一つのプロセスに割り振られる計算量が少ないが，必ず計算に参加する全プロセスを対象とした同減少し，必要とするメモリ量も減少する．このため，ある期が必要である．この同期のための通信コストは，プロセ

プロセス数を越えたところで，計算に必要なすべてのデー

ス数が増えると急激に大きくなるからである．

タがキャッシュに格納できるか，キャッシュを非常に効率

次に，並列処理におけるデータ交換のためにポイント・よく利用できる程度に減少する場合がある．横軸をプロセ

ツー・ポイントの双方向通信を用いる場合の注意点についス数に縦軸に並列性能をプロットすると，通常はプロセスて述べる．ここでは，自プロセスのデータを右隣のプロセ数が増加すると，並列オーバーヘッドの増加により性能がスに送り出す処理を send / receive のペアで実装する場合暫時低下するが，この場合，そのプロセス数のところで並を考える．まず，send / receive のペアがきちんとお互いに列性能が急によくなる．この現象をスーパースカラと呼協調しながら動作する基本的な同期通信を考える．これをび，並列計算機の性能評価を難しくしている要因の一つで一斉に右隣のプロセスにsendした後で，一斉に左隣のプロある．

セスから receive するというナイーブに実装した様子を
図２（a）に示す．一般に，send 処理は，そのデータがきちん

２．
４ 並列コードにおける通信と receive されなければ完了しない．このため，この実装で分散メモリ型の並列計算では，メモリアクセスより更には，プロセス間でデッドロックが発生してしまう．ただし，遅い計算ノード間の通信ネットワークを介して，データ交通信するデータ量が少なくて，全データが send 用のバッ換が行われる．このため，たとえ演算処理が１００％並列化でファにたまたま収まった場合，send 処理が完了する並列計

きても，通信に要する時間は並列オーバーヘッドとなり，算機システムもあり，この場合にはうまく動く．しかし，高い並列性能を得るときの足枷になる．これは，並列処理

このような正常動作が並列計算機システムの実装や問題サのプロセス数が増えると顕著になる．プロセス数の増加にイズに依存するプログラムは，可搬性が悪いので注意が必より，同時に通信すべきコネクションの本数も増えるが，要である．デッドロックを防ぐためには，図２（b）に示す

通信ネットワークのハードウェアによっては，通信の衝突

ように，例えば，まず，奇数のプロセスは右隣のプロセスが起こり，片方のコネクションが待機状態になる場合があに send し，偶数のプロセスは左隣のプロセスから receive

る．この場合，実効的な通信時間は長くなり，並列オーする．次に，偶数のプロセスが右隣のプロセスに send し，

バーヘッドの増加と並列性能の低下に直結している．通信

奇数のプロセスは左隣のプロセスから receive するよう，ネットワークに専用の高価なハードウェアを用いている並

常にハンドシェイクしなければならない．同期通信では，列計算機では，この性能低下は起こりにくい．しかし，PC各プロセス間に独立した通信チャネルが用意されている場

クラスタ型並列計算機での通信ネットワークとしてよく用合でも，全体の半分の通信チャネルしか同時に利用しない

いられている LAN 用のハードウェアでは，このような通ので，通信ネットワークのハードウェアを有効に活用して信の衝突は避けられないため，注意を要する．さらに，PCいない．そこで，すべての通信が同時に行えるよう図２

クラスタ型並列計算機では，ファイルシステムを複数の計（c）に示す非同期通信を考える．非同期通信では，send /算ノードでネットワーク共有するために NFS を用いていreceive を呼び出すと，その処理の完了を待たずに呼び出ることが多く，NFS のための通信と並列処理のための通信

し側に戻る．このため，すべてのプロセスにおいて，sendが衝突し，思わぬ性能低下に直面する．この場合，NFS/ receive 処理が起動され，全チャネルを使って通信が開始

用と並列処理用で別々の LAN を用意すべきであろう．つされる．そして，wait により，通信の完了を待ち合わせる．

まり，並列計算機では，演算速度と通信速度のバランスがwait で待ち合わせる前に，send されている変数を書き換え大事であり，通信ネットワークをなおざりにして，むやみた場合には，send を呼び出したときの変数の内容ではなに演算速度だけ速くしても，高い並列性能は得られない．

く，書き換えた後の内容が通信される可能性がある．同様

いずれにせよ，高い並列性能を得るための基本は，他プに，receive されている変数を wait 前に読み出した場合にロセスが持っているデータを使わないで，なるべく自プロ

は，本来の通信で受け取る内容ではなく，通信する前の古５３

Journal of Plasma and Fusion Research Vol.89, No.1 January 2013

い内容を読み出す可能性がある．このため，非同期通信をの並列実行を可能とするデータ並列言語 HPF（High Per-

行う場合は，wait の前にアクセスできる変数とそうでない

formance Fortran）が提案された［６］．基本的にHPFでは，変数を明確に区別しなければならないので，注意が必要でデータを各プロセッサ上にどのように配置するかのみをある．

ユーザが明示的に指示し，データ転送等のそれ以外のこと

最後に特に大規模の並列計算で有用なハイブリッド並列

はすべて処理系に任せる．このため，ユーザは煩わしいプ化について述べる．ハイブリッド並列化とは，メモリを共ロセッサ間の通信管理や実行制御を記述するプログラミン

有する計算ノード内ではスレッド並列計算を行い，計算グから解放され，比較的容易に並列計算機の高性能が享受

ノード間は分散メモリ型のプロセス並列計算を行う並列化できる．例えば，前節の２つのベクトルを読み込んで内積の手法である．並列計算を行うコア数が同じ場合，すべてを求めるプログラムをHPFで並列化すると，以下のようにを分散メモリ並列化だけで行う場合に比べて，ハイブリッ

なる．

ド並列化の方が通信チャネルの数が減るので，通信オー_______parameter(n=100)_

バーヘッドの減少が期待できる．特に，リダクション処理_______real_a(n),_b(n)に伴う同期通信を行うプロセス数が減る効果は大きい．並_!HPF$_PROCESSORS_proc(number_of_processors())

列プログラミングは，複雑になるが，大規模の場合，検討_!HPF$_DISTRIBUTE_(BLOCK)_ONTO_proc_::_a,b

する価値は十分にある．ただし，スレッド並列計算はス_______read(*,*)_a,_b

レッド（＝コア）数が増えるとメモリアクセスの制限によ_______aipd_=_0.0

り性能向上が頭打ちになるため，共有メモリ型並列計算を_!HPF$_INDEPENDENT,_REDUCTION(+:aipd)

行うスレッド数をできるだけ増やして，分散メモリ型並列_______do_i_=_1,_n計算を行うプロセス数をできるだけ減らすことが，最適な__________aipd_=_aipd_+_a(i)_*_b(i)

並列性能を与えないことには，注意が必要である．

_______end_do_______write(*,*)_’aipd_=_’,_aipd

２．
５ 並列コードの開発環境_______stop

現在，規模の大小を問わず並列計算機上で並列に動作す_______end

るプログラムを開発する場合，MPI を用いることがデファクト標準であるが，並列プログラミングは非常に煩雑なもMPI に比べると，プログラミングの容易さは圧倒的であのである．これを象徴する言葉として"Life is too short for

り，HPF には大きな期待が寄せられていた．しかし，HPF

MPI"が有名である［４］．一方で，計算ノードの最近のトレを使うためには Fortran９０クリーンなプログラムにしなけ

ンドは，マルチコア化である．今までは，コアのクロックればならず，その手間が大きかったことや，当初の HPF

周波数の向上等によって，プログラマは，黙っていても技コンパイラの性能があまりにも低く，並列化されていても術革新の恩恵を受けられた．しかし，計算ノード内のマル

並列性能が悪かったりしたため，期待が大きかった反動もチコアを活用して並列計算機の性能を享受するためには，あり，大きな失望が急速に広がってしまった．このため，根本的にプログラムを書き直して，並列化しなければなら

残念ながら，この段階からHPFが今後広く普及する可能性ない．この状況を揶揄する言 葉 と し て"The free lunch isは低いと言わざるを得ない．しかし，現在，HPF 推進協議over"がある［５］．

会［７］からフリーのHPFコンパイラが配布されており，規そこで，従来のプログラムに最小限の付加的な指示行を

則正しい差分系スキームのプログラムなら，簡単に並列化追加するだけで分散メモリ型の並列計算機上でプログラムができ，見劣りしない並列性能も得られるため，分散メモ

図２：プロセス間のポイント・ツー・ポイント双方向通信．
（a）
ナイーブな同期通信，（b）
ハンドシェイク同期通信，（c）
非同期通信

５４

Lecture Note

2. Elementary Technique to Develop Parallelized Codes

リ型並列処理の入門としては，非常に有用である［８］．

H. Sakagami

XMP は，MPI と対立・競合するものではなく，共存できる

一 方，並 列 処 理 を 記 述 す る た め Coarray と い う 概 念ものとするため，MPI ライブラリと混在できる仕様としてが，Fortran２００８の国際規格に導入された［９］．Coarray といる．この機能により，並列化の大枠は，XMP を用いて容は，各プロセスが自分のメモリ空間を供出し，全体として易に並列化し，特に細かなチューニングが必要な場所だけグローバルな大域アドレス空間を作り上げ，他プロセスの

MPI ライブラリを用いて性能向上を図ることで，高並列・メモリ空間を容易にアクセスできるように，いわば共有メ

超並列でも実用的な性能が期待できる．一方で，コンパイ

モリ風に見せる仕組みである．逆に見れば，グローバルア

ラによる自動並列化は，並列プログラミングに不慣れなドレス空間は，実際には複数の計算ノードに分割されて保ユーザには有効であり，自動化だけで十分な性能を達成で持されているため，これを強調して Coarray は Partitioned

きるアプリケーションも，わずかではあるが存在する．しGlobal Address Space（PGAS）と 呼 ば れ る こ と が 多 い．

かし，高度な自動化が行われるほどプログラムとその動作

HPF では，元々の計算したい大きなサイズの問題を小さくに対する理解が困難となり，期待している効果を得るため分割して，それぞれを複数のプロセスに分担させるイメーには，どのようにプログラミングすればいいのかが不明瞭

ジであるが，Coarray では，それぞれのプロセスが計算すなため，ユーザによる性能チューニングは難しくなる．まる小さなサイズの問題を寄せ集めて大きなサイズにするイ

た，処理系のバージョンや動作条件により，並列性能が大メージである．実際の他プロセスへのメモリアクセスは，

きく異なる可能性が高くなり，同一プログラムにおける並

put / get と呼ばれる単 方 向 通 信 で 実 装 さ れ る こ とが多列性能の互換性が悪くなる．そこで，XMP では，並列性能

く，他プロセスのメモリ空間上に直接書き込んだり，読みの互換性を重視し，プログラムからその動作が容易に想像出したりできる．双方向通信では，送り手と受け手が協調できるレベルまで，利用者が明示的に書き下すことができして動作する必要があるため，効率が悪くなりがちである仕様としている．以上の基本方針の下に，現在，XMPる．しかし，一方的に動作する単方向通信は，メモリを操

では，グローバルな名前空間と変数を計算ノード間に分散作される側の都合を考える必要がなく，効率のよい通信が配置するイメージをプログラマに提供するグローバル期待できる．ただし，これはメモリのコンシステンシをプビューと MPI ライブラリ呼び出しの引数として記述でき

ロセス間で伝達する特別なハードウェアが実装されている

るローカル変数のビューであるローカルビューを提供し，場合で，そのようなハードウェアを持たない並列計算機でこれらの変数や関連する仕様が矛盾なく混在できる言語仕

単方向通信を行うと，双方向通信よりかえって性能が劣化様とした．グローバルビューでは，通常の逐次コンパイラするので注意を要する．

を使えば同じ結果を出す逐次実行が可能になるよう指示文

さて，Coarray を使ったプログラムを以下に示す．

によりベース言語（Fortran および C）を拡張している．そして，データ並列プログラミングモデルとワークシェアに_real_::_r[*]____!_Scalar_co-array

よって，典型的な並列化をサポートする．個別のプロセス_real_::_x(n)[*]_!_Array__co-array_を意識してプログラミングするローカルビューとしては，_real_::_t_______!_Local_scalar

Coarray をベースにした PGAS 機能を提供し，メッセージ_integer_::_p____!_Local_scalar_

通信とリモート・メモリ操作を可能としている．そして，!_GET_communication

データの通信やプロセス間の同期等は，すべて明示的に記_t_=_r[p]_述し，並列性能のチューニングについてユーザがわかりや_x(:)_=_x(:)[p]すく工夫できるようにする．また，柔軟性と拡張性を実現!_PUT_communication

するために，基本的な実行モデルは MPI と矛盾なく共存で_x(:)[p]_=_r

きるようにし，より複雑に並列性能をチューニングされた

GET では，id=p のプロセス（Coarray では，プロセスを

MPI サブルーチンや MPI プログラムの一部分を直接利用

image，ID を image 番号と呼ぶ）から，スカラ変数 r の値可能にしている．また，マルチコアあるいは SMP クラスタ

を自身の t に，配列 x の全要素を自身の x に読み込んでいでは，OpenMP を用いてスレッドプログラミングができる

る．また，PUT では，自身のスカラ変数 r の値を id=pようにし，シームレスなハイブリッド並列化のためのプロのプロセスの配列xに書き出している．Coarrayでは，並列

グラミングを可能としている．

計算のためのデータ交換を Fortran 言語レベルで記述はで

２．
６ まとめ

きるが，MPI と同様に，ユーザが明示的に必要な通信をす
べてコーディングしなければならないことからは免れるこ

ノード間の通信に専用のハードウェアを実装している高とはできず，やはり，ユーザに大きな負担を要求する．こ性能な並列計算機だけではなく，コ モ デ ィ テ ィ・コ ンのような状況を鑑み，逐次プログラムからシームレスに並ピュータを安価なネットワークで結合した PC クラスタ型

列化および高性能化を支援する並列実行モデルの確立とそ

並列計算機でも，分散メモリ型のアーキテクチャが主流で

れ に 基 づ く 超 並 列 プ ロ グ ラ ミ ン グ 言 語 XcalableMPある．このため，並列コードを並列計算機上で実行し，そ（XMP）の仕様策定と処理系の開発が行われている［１０］．

の性能を十分に享受するためには，分散されたメモリを前

すでに MPI で書かれている膨大な並列プログラムをすべ提として並列コードを開発しなければならない．この並列て新言語に書き換えることは非現実的である．このため，

プログラミングでは，並列計算機の内部アーキテクチャま５５

Journal of Plasma and Fusion Research Vol.89, No.1 January 2013［３］http://www.mpi-forum.org/［４］T-shirts message, Workshop on OpenMP Applications
and Tools, Purdue University, West Lafayette, Indiana,
US, July 30-31 (2001).
［５］H. Sutter, Dr. Dobb's Journal 30, (2005).
［６］High Performance Fortran 2.0 公式マニュアル（シュプリ ン ガ ー･フ ェ ア ラ ー ク 東 京， ISBN4-431-70822-7，
1999）．
［７］http://www.hpfpc.org/［８］PC クラスタで並列プログラミング―High Performance
Fortran で楽々並列化（培風館，２
０
１
１）
，
ISBN９
７
８
‐
４
‐
５
６
３
‐
０
１
５
８
６
‐
２(2011).
［９］http://www.co-array.org/［１０］http://www.xcalablemp.org/

でも考慮しなければならず，非常に煩雑であり，開発者に大きな努力を要求する．しかし，それが悲しい現実であり，
並列コードの開発者が並列プログラミングを始めるとき，本章がその理解の一助になれば幸いである．そして，この状況を打破すべく，HPF が普及しなか っ た 原 因 を分析し，その失敗の経験に学んで言語仕様が策定され，処理系の 開 発 が 進 ん で い る 超 並 列 プ ロ グ ラ ミ ン グ 言 語 XcalableMP の今後の発展に期待していただきたい．

参考文献［１］B. J. Smith, Int. Supercomputing Conf. 07, keynote speech,
Dresden, Germany, June 26-29 (2007).
［２］http://openmp.org/!!!!!!!!!!!!!!

さか

がみ

ひと

し

坂 上 仁 志!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!!!!!!!!!!!!

核融合科学研究所基礎物理シミュ
レーション研究系，教授．仕事では，
レーザー核融合に関連する粒子および流体シミュレーション，大規模並列計算の研究に従事しています．趣味では，「日本の秘湯を守る会」加盟の温泉旅館を訪ね歩いたり，未だにスキーをしたり，海外旅行も楽しんでます．

!!!!!!!!!!!!!!!!!!!!!!!!!!

５６