卒業研究論文

並列コンピュータ・ソフトウェアの性能
並列コンピュータ・ソフトウェアの性能評価
性能評価

２００２年２月５日
高知工科大学
知能機械システム工学科
磯村研究室

片山 直人

1020102

-1-

片山直人

目

次
ページ

まえがき

3

１．並列コンピュータ・システムの構成

4２．ハードウェア構成

6３．並列計算テストプログラム

7

４．テスト結果

9

結言

11

謝辞

12

参考文献

12

付録 A メッセージパッシング・インターフェイス（MPI）

13

1020102-2-

片山直人

並列コンピュータ・ソフトウェアの性能評価
まえがき
コンピュータによる計算はより速くすることが常に求められてきた。高速計算が必要とされる科学や工学におけるシミュレーションの分野では、しばしば有効な結果を得るために大量のデータに対して多数回の繰返し計算が必要なことが多い。現在の製造現場では、工学的計算やシミュレーションは秒あるいは分単位の時間でできなければならない。設計者が効率的に作業するための時間は短いので、解に到達するのに１ヶ月もかかるシミュレーションは設計環境では通常受け入れられない。
システムが複雑になればなるほど、それをシミュレートする時間はますます増加するし、計算に特定の締め切り時間があることもある。翌日の株価を正確に予測するのに２日かかったら予測は役にたたない。大きな分子構造のモデリングや広域の天気予報のような問題は、現在、コンピュータでは適切な時間内には解けない問題で、実行に１０年を要するよ
うでは話にならない。
スーパーコンピュータはそのために開発された。しかしこれは非常に高価である。一つの解決策は安価なパソコン(PC)やワークステーション(W/S)をローカルエリア・ネットワー
ク(LAN)で接続した並列コンピュータである。
ちなみに USA の国防省では原爆のシミュレーション用として、２５６CPU×３２システ
ム＝８１９２CPU 並列コンピュータを予算２億ドルで計画中である。
当研究室では脳の記憶・学習機能のシミュレーションを目的としてUNIX マシン８CPU
Windows NT マシン１６CPU
計２４CPU の異機種間接続並列コンピュータシステムを試作、並列処理ソフトウェアを開発した。
テストプログラムの例として１．素数およびその個数の探索
２．マルチシリンダーエンジン動特性シミュレーション
３．３次元波動方程式の求解と CG 表示
を取り上げ、このソフトウェアの評価テストを行っている。
本稿では、８台の並列 CPU を使用し、オイラーの関数すなわち素数の個数計算を例にとり、並列計算ソフトウェアの性能評価を行った結果について述べている。素数は、現在インターネット上を行き交う情報の暗号化に重要な役割を演ずる数である。
CPU 間の通信時間が CPU の処理時間に比べ無視できるときには、２台の並列計算が計
画通り機能すれば、８台でも可能であり、８台が可能ならば１００台でも８１９２台でも可能である。ただし、無限の台数とはいかない。LAN の処理時間が CPU１台の処理時間にせまるような大量のデータのやりとりがあると、当然システムのスループットは低下する。

1020102

-3-

片山直人

１

並列コンピュータ・システムの構成
システム構成として次の三つが提案された。
（１）共有記憶マルチプロセッサシステム（２）分散共有記憶マルチプロセッサシステム（３）メッセージ通信マルチプロセッサシステム

１−１ 共有記憶マルチプロセッサシステム
単一プロセッサモデルを自然に拡張すると複数のプロセッサを複数のメモリに結合し、各プロセッサがどのメモリモジュールもアクセスできるようにする図１に示すような仮想共有記憶構成が得られる。
プロセッサとメモリ間の結合は何らかの形の結合網によっている。共有記憶マルチプロ
セッサシステムは単一アドレス空間を用いていて、それは主記憶のすべての位置が一意のアドレスをもち、プロセッサは各位置のアクセスにそのアドレスを使うことを意味する。
CPU

CPU

CPU

CPU

#1

#2

#3

#4

CPU
・・・・・・・・・・・・・#n・・・・・・・・・・・・・・

Mn

ネットワーク

M1

M2

M3

M4

仮想共有記憶
図１仮想共有記憶マルチプロセッサ構成
この方式では仮想共有記憶が一つのメモリとして機能し、全ての CPU からアクセス可能となる。
（２）分散共有記憶マルチプロセッサシステム
各プロセッサが単一のアドレス空間を用いて全メモリをアクセスする分散共有記憶システムである。
この場合、プロセッサが自分のローカルメモリにない場所をアクセスするときは、デー

1020102-4-片山直人

タを他のプロセッサから受け取るのに、メモリが分散しているという事実を隠すために何らかの形で自動的にメッセージ通信を発生させなければならない。（図２）

ネットワーク

＃１

＃２

＃ｎ

M１

M2

Mn

図２分散共有記憶マルチプロセッサ構成
したがって、この構成では見かけ上、各メモリＭ1，Ｍ2，Ｍ3，
・・・Ｍm が全ての CPUからアクセスできるようにネットワークが機能しなくてはならない。
（３）メッセージ通信マルチプロセッサシステム
マルチプロセッサシステムのもう一つの形は、図３に示すように、完全なコンピュータを結合することによってつくれる。各コンピュータはプロセッサと他のプロセッサからはアクセスできないローカルメモリで構成される。すなわち、マルチコンピュータではメモ
リはコンピュータ間に分散され、各コンピュータは自分のアドレス空間を有し、プロセッ
サは自分のメモリのロケーションのみをアクセスできる。プロセッサが他のメモリにメッセージを送信できるように結合網が用意されている。このメッセージには他のプロセッサの計算に必要なデータが含まれる。この種のマルチプロセッサシステムは通常メッセージ
通信マルチプロセッサとよばれ、特に独立して動作できるそれだけで完結したコンピュー
タから構成されている場合には単にマルチコンピュータともよばれる。このメッセージ通信マルチコンピュータにおけるメッセージは一つのプロセッサから他のプロセッサへプロ
グラムの指定に従ってデータを運ぶ。

1020102-5-

片山直人

ネットワーク

＃１

＃２

＃ｎ

M１

M2

Mn

図３メッセージ通信マルチプロセッサシステム
メッセージ通信マルチコンピュータのプログラミングでは問題を同時に実行する部分に分割する。このプログラミングには並列言語や拡張逐次言語を使うこともできるが、よく行われる方法はメッセージ通信ライブラリルーチンを用いるものである。問題の独立した並列実行可能な部分をしばしばプロセスとよぶ。
すなわち、問題を多数の並列プロセスに分割する。プロセスはそれぞれ別のコンピュー
タで実行してもよく、６個のプロセスと６台のコンピュータがあるならそれぞれのプロセ
スを１台のコンピュータで実行すればよいが、もし、コンピュータ数よりプロセス数が多ければ複数のプロセスを１台のコンピュータ上で時分割実行することになるだろう。プロ
セスはメッセージを送信することで通信し、それが、データや結果をプロセス間に分配する唯一の方法である。我々の研究室でこのシステムを採用し、メッセージ通信には MPI（Message Passing Interface）ライブラリの一つ MPICH を ANL（Argonne National
Laboratory)からダウンロードして使用した。
２ ソフトウェア評価用ハードウエア構成
連携研究センターには次のコンピュータが設置され、LAN で接続されている。このうち、本研究に使用したワークステーションは○印で示した。
Compaq（ES40）

並列コンピュータ（Alpha１＆ Alpha2）
５００ＭＨｚＸ４ＣＰＵＸ２＝４GHz８CPU
２ＧＢ主メモリ X2
３６ＧＢX2 補助メモリ○Compaq（SP750 ＆ AP550X2）
７３３ＭＨｚ X３1020102-6-片山直人

２５６MＢ主メモリ X3
１４０ＧＢ ＆ １８GBX2 補助メモリ○INTERGRAPH（Viｓｕａｌ W/SX2）
７５０ＭＨｚ X2
２５６MＢ主メモリ X3
１０GBX2 補助メモリ○Sｉｌｉｃｏｎ Ｇｒａｐｈｉｃｓ （Ｖｉｓｕａｌ ＷＳ ５４０）
５５０ＭＨｚ
２５６MＢ主メモリ
８ＧＢ補助メモリ○INTERGRAPH（W/S）

７５０ＭＨｚ
２５６MＢ主メモリ
９GB 補助メモリ

SUN（Ultra10）

４４０ＭＨｚ５１２MＢ主メモリ
９GB 補助メモリ○NEC(Express 5800)

５５０ＭＨｚ
２５６MＢ主メモリ
１３GB 補助メモリ○Compaq （AP550）

５５０ＭＨｚ
２５６MＢ主メモリ
９GB 補助メモリ

LAN 上における各ワークステーションのＩＰアドレスは次の通りである。
①Alpha1

100.0.0.10②Alpha2100.0.0.11③SP750100.0.0.31④AP550-1

100.0.0.30⑤AP550-2

100.0.0.34⑥Intergraph1

100.0.0.1⑦Intergraph2

100.0.0.2⑧Silicon540

100.0.0.33⑨Intergraph3

100.0.0.3⑩SunUltra10

100.0.0.20⑪NEC

100.0.0.32⑫AP500100.0.0.35

３ 並列計算テストプログラム

1020102-7-片山直人

８台のワークステーションを LＡＮで接続した並列コンピュータ・システムの性能をテストするため、1 からｎ（ここではｎ＝８００００００）までの数の中に素数がいくつあるか
数えるオイラー関数のテストプログラムを使用した。
ちなみに、桁数の大きな素数はインターネット上を行き交う情報の暗号化と解読に大きな力を発揮する公開鍵暗号に使われる。大きな素数の掛け算は簡単であるが、その積が与えられて因数に分解するのは極めて難しい。このように逆算の困難は関数のことを一方向関数といい、オイラー関数はそのような関数の一例である。
整数ｎに対し、1，2，3，．
．
．
，ｎ−1 のうちでｎと互いに素なものの個数をφ（ｎ）で表し、これをオイラーの関数という。これは 1/ｎ，2/ｎ，3/ｎ，……,ｎ−1/ｎのなかの既約分数の個数に等しい。
３−１素数の個数を求める並列計算アルゴリズム
素数の個数を求めようとしている数ｎをmin0〜max0
min1〜max1
min2〜max2
・・・
min7〜max7というように８グループに分け、#0 から#7 までの CPU に配分する。
#0 CPU をマスターとし、#１〜#7CPU はスレーブとする。#0 CPU は数 mini と数 maxi
（I=１，2，．
．
．
，7）を作り、#ｉCPU に mini と maxi を送る。受け取った#ｉCPU は miniから maxi までの数に素数がいくつあるか調べて結果を#0 CPU すなわちマスターに送り返す。
#0 CPU は min0 から max0 までの素数を調べると同時に#ｉCPU（ｉ=1，2，
．．
．
，7）か
ら受け取った素数の数を集計する。
素数の個数を求めるアルゴリズムは次の通りである。
（１）素数の個数のカウンタをリセット（２）数ｎを読み込む（３）１及びその数自身（＝ｎ）以外の数ｉ（1<ｉ<ｎ）で割ったとき、剰余Ｒ（ｎ/ｉ）
が 0 になるような数が一つでも存在するときは、素数ではないのでカウンタはそのままで進めない（４）存在しないときは素数なのでその値をセーブしカウンタを一つ進める（５）(1)へ戻る

1020102-8-片山直人

３−２アプリケーションプログラムのインストール
前節のアルゴリズムにしたがってプログラムを書き、マスターＣＰＵ上でコンパイルする。コンパイラは visual C++を使用した。ビルドを行って実行可能なプログラムを作成、7台のスレーブ CPU に送り込む。
３−３ 実行
1 台のマスター、7 台のスレーブ CPU の名前、プロセスのディレクトリ、実行プログラ
ムファイル名を記入したコンフィギュレーションファイル conf.txt を作る。MPI 実行コマ
ンド MPIRun conf.txt で CPU から全てのプロセスを起動する。
４ テスト結果(1)一台での実行時間
もっとも遅い Compaq Ａｐ500 一台での実行時間は 1865 秒(約 31 分)、素数の個数は 63951 であった。
（2） 最初のトライアル計算
n=800,000 として与えた。最初は m 個の CPU に均等配分してn１(1)=n2(1)= n３(1)=……=nｍ(1)=n/mとする。N=800,000、m=8 なので
n１(1)=n2(1)= n３(1)=……=n８(1)=100,000
である。
実行結果は次のようになった。
コンピュータ名

τi(1)φ(ｎ）：素数の個数

Intergraph 1

30 sec9592

Intergraph 2

75 sec8392

Intergraph 3

110 sec

8013

Compaq Ap550-1

150 sec

7863

Compaq Sp750

190 sec

7678

Silicon 540

305 sec

7560

NEC

360 sec

7445

Compaq Ap500

410 sec

7408

計

63951

処理時間に 10 倍以上開きがあるほど計算機負荷がアンバランスでは、並列化のメリットが十分ではない。1 台での処理時間 1865 秒に対し、410 秒は 1/4.5 にすぎない。

1020102-9-片山直人

（3）２回目の配分
２回目の配分 n１(２)、n2(２)、 n３(２)、……、n８(２) は次のようにして求められる。
(τ1(1)/n1(1))x1=(τ2(1)/n2(1))x2=…=(τm(1)/nm(1))xm=τ(1)τ(1)Σ(ni(1)/τi(1)) =n(i=1, 2, 3, ……, m)

τ(1) =n/Σ(ni(1)/τi(1))(i=1, 2, 3, ……, m)

xi=n/Σ(ni(1)/τi(1))・ni(1)/τi(1)(i=1, 2, 3, ……, m)ni(2) = xi

(i=1, 2, 3, ……, m)

計算結果はコンピュータ名ni(2)Σni(2)

Intergraph 1

351025351025Intergraph 2

140410491435Intergraph 3

95734

587169Compaq Ap550-1

67940

655109Compaq Sp750

55425

710534

Silicon 540

34527

745061

NEC

29253

774314Compaq Ap500

25686

800000となる。このデータを入力した実行結果は次のようになった。
コンピュータ名

τi(２)φ(ｎ）：素数の個数

Intergraph 1

270 sec

30053

Intergraph 2

243 sec

10824

Intergraph 3

210 sec

7262

Compaq Ap550-1

190 sec

5069

Compaq Sp750

165 sec

4137

Silicon 540

140 sec

2537

NEC

123 sec

2175

Compaq Ap500

110 sec

1894

計

63951

負荷のアンバランスはかなり改善されたが、2 倍以上の差があり、まだ十分とは言えない。計算時間も 270/1865=1/6.9 で理論値 1/8 に及ばない。
（4）３回目の配分
Σ(ni(2)/τi(2)) =351025/270+140410/243+ 95734/210+67940/190

1020102- 10 -

片山直人

+55425/165+34527/140+29253/123+25686/110=3745.235
ni(3)=n/Σ(ni(2)/τi(2))・ni(2)/τi(2)(i=1, 2, 3, ……, m)の計算結果は次のようになる。
コンピュータ名ni(2)Σni(2)

Intergraph 1

277706277706

Intergraph 2123425401131

Intergraph 3

97377

498508Compaq Ap550-1

76381

574889Compaq Sp750

71752

646641

Silicon 540

52679

699320NEC

50801

750121Compaq Ap500

49879

800000

このデータを入力した実行結果は次のようになった。
コンピュータ名

τi(３)φ(ｎ）：素数の個数

Intergraph 1

175sec24258

Intergraph 2

178sec9683

Intergraph 3

180sec5374

Compaq Ap550-1

170sec5777

Compaq Sp750

178sec5374

Silicon 540

190sec3929

NEC207sec3748

Compaq Ap500

210sec

3705

計

63951

最長と最短の処理時間の差は 210‑170=40 秒に減少した。したがって、並列化により、計算速度は 1865/210≑ 9 倍に改善された。
結言
8 台のワークステーションを LAN で接続、MPICH ライブラリを用いて並列化したシス
テム上で、数８０００００にある素数の個数を求めるテストプログラムを走らせ性能をテストし、次の結果を得た。
１．各 CPU の負荷が均一になるよう数を配分すると、計算時間は一台で実行した場合に比べ約１/9 になった。
２．ｎ＝８０００００を等分して分担した場合、７００００１から８０００００を分担

1020102- 11 -

片山直人

する CPU がもっとも時間がかかり、４１０秒であった。
３．最も少ない時間を要したのは１〜１０００００を分担した。CPU で３０秒で、CPUの負荷のアンバランスは１０倍を超えた。
４．再配分を行ったところ最長時間を要したのは１〜３５００００を負担した CPU で計算時間は２７０秒で、アンバランスは３倍以下に減少した。
５．再々配分を行った結果、最長と最短の処理時間差は 210‑170=40 秒となり、かなり均一化した。210 秒という並列処理時間は１台の CPU 処理時間 1865 秒に比べ 1/9 に相当する。
今後の課題として、個数探索の初期段階で配分を最適化するアルゴリズムの開発が考えられる。
謝辞
本研究では、磯村研究室で開発中の並列コンピュータ・システムの一部を使用した。担当
教員の磯村教授に感謝する。
参考文献
Barry Wilkinson and Michael Allen；Parallel Programming、1999 by Prentice-Hall, Inc.

参考ウェブサイト
Argonne National Laboratory , http://www.anl.gov/
MCS: Mathematics and Computer Science Division,

http://www-fp.mcs.anl.gov/division/welcome/default.asp
MPI Forum , http://www-unix.mcs.anl.gov/mpi/
MPICH Home Page , http://www-unix.mcs.anl.gov/mpi/mpich/1020102- 12 -

片山直人

付録 A メッセージパッシング・インターフェイス（MPI)
２つのプロセスの間でのメッセージのやりとりは、比較的分かりやすい概念である。あ
えて別の見方を挙げると、メッセージ通信は、あるプロセッサのメモリ空間から、もう一方のプロセスのメモリ空間へのデータの転送とみることもできる。もちろん一般には、メッセージをやりとりするのは２つのプロセスとは限らず、もっと多くのプロセスが関与し得る。
分散メモリ型の並列計算機では、メッセージ通信によるプログラミングが最もハードウ
ェアの構成に即したプログラミングであり、最高の性能が期待できる。また、共有メモリ型の並列計算機でも、メッセージ通信で書いたプログラムを実行するのは容易である。
MPI は現在最も主要なメッセージ通信ライブラリの一つである。
MPI は非常に豊富な機能を持つシステムであり、MPI のごく基本的な使い方と、利用頻度が高いと思われる、３つ以上のプロセスによる集団通信のための関数群を中心に述べる。
MPI は歴史的経緯により、基本仕様（MPI-1)と拡張機能仕様（MPI-2)という２つの部分に分かれているが、MPI-1 のみを扱う。また、MPI は Fortran, C および C++の３つの言語からの利用形式を規定しているが、ここでは、C および C++のみを使用した。
MPICH は、恐らく最も重要な MPI ライブラリである。アメリカのアーゴン国立研究所（Argonne National Laboratory)が模範実装として開発し、無償でソースコードを配布している。移植しやすさを重視したつくりになっているため盛んに移植が行われ、日本を含め、世界中のほとんどのベンダの並列マシンの上で利用できる。また LAN 環境でも利用できる。
MPI-1 には 127 個の関数があるが、最低６個の関数だけで、任意個のプロセスの間での１対１通信を始めることができる。ここでは、これら６個の関数に限定した。
１対１の通信があれば、それを組み合わせることで、任意の通信パターンを表現できる。
しかし、MPI には、１対１の通信のほかに、様々な通信パターンのための関数がある。こ
れら通信パターン向け関数を使うことは、単にプログラムがすっきりするだけでなく、より高い性能を得ることができる。
最初に、通信が起きる舞台となる環境について述べる。ここで考える並列処理は、幾つかのプロセスによる並列処理である。幾つかのプロセスが強調して、１つのアプリケーシ
ョンを実行する。これらのプロセスは、すべて同じ計算機上にあるかもしれないし、幾つもの計算機上にあるかもしれない。分散している場合でも、そのうちの幾つかのプロセス
は、同じ計算機上にあるかもしれない。
１つのアプリケーションの実行にかかわる全プロセスは、同じプログラムを実行しているかもしれないし、幾つかのプロセスは他のプロセスとは違うプログラムを実行しているかもしれない。
こういったプロセスとプログラムの配置は、MPI-1 では、前提として、アプリケーショ
ンの実行が始まる前にすべて決まっていることになっている。MPI-1 では、１つのアプリ

1020102- 13 -

片山直人

ケーションの実行にかかわる全プロセスは、一斉に実行を開始し、また、一斉に終了する。
その総数はアプリケーションの処理が続く間、変わらない。言い換えると、MPI-1 を使って新たなプロセスを起動したり、プロセスを終了させたりすることができない。MPI-2 では新しいプロセスを起動することができるが、ここでは扱わない。
では、プロセスとプログラムの配置はどうやって決めればよいか。これは、MPI プログ
ラムを実行するために使う並列プラットフォームに依存する。すなわち、並列マシンや LAN
環境などのハードウェアと、その上の OS など、並列実行環境を提供するソフトウェアとして何を使うかによって、その方法が異なる。ここでは、代表例として、MPICH を LAN 環
境で使う場合について述べる。
MPI プログラムはコマンド行でスタートされ、たとえば、実装に依存してmpirun prog1-np4
または、
prog1-np4
により、同じ実行可能プログラムを別のプロセッサで同時に起動できる。
これらのコマンドは実行可能プログラム prog1 の４個のコピーをスタートするが、どこでそれを実行するかについては何も指定していない。プロセスのプロセッサへのマッピング
も MPI 標準には定義されていなくて、必要なら実装ごとに別に提供しなければならない。
特定のマッピングはコマンド行で指定されたり、実行可能プログラムの名称とそれを実行するプロセッサの名を保持するファイルを用いて行われる。
はじめて MPI 関数コールをする前にコードを MPI_Init()で初期化し、すべての MPI 関
数コールの後で MPI_Finalize()で終了させねばならない。コマンド行引数を MPI_Init()に渡して MPI のセットアップ操作をすることができる。すなわち
main (int argc, char *argv[ ]){MPI_Init(&argc, &argv);/*initialize MPI */．
．
．
MPI_finalize();/*terminate MPI */}（逐次の C プログラムと同様に、&argc、すなわち引数カウントは引数の個数を与え、&argv、
すなわち引数ベクトルは文字ストリングの配列へのポインタである。
）
最初は、全プロセスは MPI_COMM_WORLD とよばれる「世界」に登録されていて、それぞれ一意のランク（ｎプロセッサの場合０からｎ−1 の番号）を与えられている。MPIの用語では MPI_COMM_WORLD は通信操作のスコープを定義するコミュニケータ
（communicator）で、プロセスはコミュニケ−タに関連づけられたランクを有する。プロ

1020102- 14 -

片山直人

セスのグループをつくるためにこれ以外のコミュニケ−タを設定することもできるが、簡単なプログラムではデフォールトのコミュニケ−タ MPI_COMM_WORLD で十分である。
しかし、この概念によってメッセージに対して別のスコープを有するプログラム、特にライブラリを構築することが可能である。
A−１ 単一プログラム複数データモデル（SPMD）
このモデルは各プロセスが事実上同じコードを実行するときに理想的であるが、普通はあらゆる応用において 1 個あるいはそれ以上のプロセッサが別のコードを実行する必要がある。一つのプログラムでそれをできるようにするためには、それぞれのプロセッサがコードのどの部分を実行するかを選択する文を挿入する必要がある。よって、SPMD モデル
でも、マスタコードとスレーブコードを同じプログラム内に置かねばならないためにマスタスレーブアプローチを使えないということはない。次の部分 MPI コードはどうすればそれができるかを示している。
main （int argc，char *argv[ ]）｛MPI_Init（&argc， &argv）；．
．
．
MPI_Comm_rank（MPI_COMM_WORLD， &myrank）；/*find process

rank */

if （myrank==0）
master（）；else
slave（）；．
．
．
MPI_Finalize（）；｝
ここで、master()と slave()はそれぞれマスタプロセスとスレーブプロセスによって実行される手続きである。この方法は３個以上のコード列に対しても使うことができる。SPMD
モデルは、各プロセッサが完全に異なるコードを実行する場合はメモリ利用の面で非効率的であるが、幸いなことにその必要性はあまりない。SPMD モデルのもう一つの利点はコ
マンド行引数を各プロセスに渡せることである。
SPMD モデルを使うことにすると、変数の大域宣言は各プロセスで複製することになり、1020102- 15 -

片山直人

複製されない変数は局所的に宣言する必要がある。すなわち、そのプロセスでのみ実行されるコード内で宣言する。たとえば、
MPI_Comm_rank（MPI_COMM_WORLD， &myrank）； /*find process rank */if （myrank==0）｛/*process 0 actions/local

variables */int x，y；
．
．
．
｝else

if

（myrank==１）｛/*process１

actions/local

variables*/
int x，y；．
．
．
｝
ここで、プロセス０の x と y はプロセス１の x と y とは異なる局所変数である。
データの大きさが変化する場合には、メモリは malloc()を用いて動的に割り付けられる。
たとえば、
if （myrank==0）｛
int *data;
data = ( int *)malloc(nproc * sizeof ( int) );.
.
｝A−２メッセージ通信ルーチン
メッセージ通信は誤った操作の原因になることがあり、MPI は安全な通信環境を提供することを意図している。安全でない通信の例を図 A1 に示した。この図において、プロセス
０はプロセス１にメッセージを送信しようとしているが、ライブラリルーチン間でもメッ
セージ通信がある。この場合、各 send/recv の組は発信元と宛先がマッチしていても誤ったメッセージ通信が発生する。ワイルドカードを用いると、正しくない操作やデッドロック
はさらに起きやすくなる。一つのプロセス内の非ブロック型受信の発信元とタグが両方ともワイルドカードで、別の二つのプロセスの組がメッセージ通信を必要とするライブラリ
コールをしたとしよう。このライブラリルーチンの最初の送信はワイルドカードを用いて1020102- 16 -

片山直人

いる非ブロック型ルーチンとマッチして誤った動作をすることがありうる。
MPI では、すべての１対１および集合的メッセージ通信においてコミュニケ―タが用いられる。コミュニケ―タとは相互に通信することが許されるプロセスの集合を定義する通信領域である。これを用いれば、ライブラリの通信領域をユーザープログラムの領域と分離することができる。各プロセッサはコミュニケ―タ内で０から n-1(n はプロセッサ数）までの整数であるランクを有する。実際にはコミュニケ―タには、グループ内の通信用のイ
ントラコミュニケ―タ(intracommunicator)とグループ間の通信用のインターコミュニケータ(intercommunicator)の２種類がある。グループは、この目的のためにプロセスの集合を定義するために用いられる。プロセスはグループ内で一意のランク（グループ内プロセ
ッサ数が m のとき０から m-1）を有し、一つのプロセスが複数のグループのメンバになれる。ただし、簡単なプログラムではイントラコミュニケ―タを使いさえすればよく、グループについてのその他の概念は不要である。

lib ( )

プロセス０

プロセス1

宛先

宛先

send(…,1,…);send(…,1,…);lib ( )
recv(…,0,…);recv(…,0,…);（a）意図した動作

lib ( )

プロセス０

プロセス1

宛先

宛先

send(…,1,…);send(…,1,…);lib ( )
recv(…,0,…);recv(…,0,…);（b）ありうる動作
図 A! ライブラリによる安全でないメッセージ通信

1020102- 17 -

片山直人

応用内のプロセスに対する最初のコミュニケ―タとしてデフォルトのイントラコミュニ
ケ―タ MPI_COMM_WORLD があるので、新しいコミュニケ―タをつくる必要はない。簡単な応用では MPI_COMM_WORLD をすべての１対１および集合的操作に利用できる。新
しいコミュニケ―タは既存のコミュニケータをもとにしてつくられ、既存コミュニケ―タからコミュニケ―タ（および既存グループからグループ）を形成する MPI ルーチン群が用意されている。
A−３ １対１通信
メッセージ通信はよく知られている送受信コールによって行われる。メッセージタグがあ っ て 、 受 信 ル ー チ ン の タ グ お よ び 発 信 元 の か わ り に (MPI_ANY_TAG とMPI_ANY_SOURCE)を使うことができる。
パラレル・バーチャル・マシン(PVM)スタイルのデータのパッキングとアンパッキング
は、メッセージの発信元または宛先とともにパラメータ内に定義される MPI データ型を用いて通常避けられる。そのデータ型は基準の MPI データ型（MPI_INT, MPI_FLOAT,MPI_CHAR など）でもユーザーがつくったものでもよい。ユーザー定義のデータ型は既存のデータ型からつくられる。これによって任意の複雑さのメッセージを形成するための構造をつくりだせる。
たとえば、２個の整数と１個の浮動小数点を１メッセージで送信したければ、それを含む構造をつくることができる。パック/アンパックルーチンの必要性をなくすほかにも、デー
タ型を宣言して用いればその再利用ができるという利点もある。また、明示的送受信バッ
ファも必要ない。これは発信元内から明示的送信バッファへのコピーをなくすので、大きなメッセージに対する記憶要求を減少させる点で特に有用である。
送信バッファへのコピーは時間的不利益および記憶場所を２倍使うという問題がある。
A−４完了
送受信ルーチンはいくいつかの種類があって、局所完了と大域完了という概念で分類される。ルーチンは、その操作の自分の部分をすべて終了したとき局所完了となり、その操作にかかわるすべてのものが自分の部分を完了したとき大域完了となる。すなわち、操作が大域完了となるためにはすべてのルーチンが局所完了でなければならない。
A−５ブロック型ルーチン
MPI ではブロック型ルーチン（送受信とも）は局所完了したときリターンする。ブロッ
ク型送信ルーチンの局所完了条件は、メッセージを保持していた場所が再利用できる、すなわち、送信されるメッセージに影響することなく変更できるということである。ブロッ
ク型送信はメッセージを送信してリターンするが、これはメッセージが受信されたことを1020102- 18 -

片山直人

意味するわけではなく、プロセスがメッセージに悪影響を与えることなく先へ進めることだけを意味する。要するに発信元プロセスはデータアクセスに必要な最小時間だけブロックされる。ブロック型受信ルーチンも局所完了したときにリターンするが、この場合はメッセージが宛先に受信され、それをリードすることができることを意味する。
ブロック型送信のパラメータの一般形は以下の通りである。

MPI_Send(buf, count, datatype, dest, tag, comm)送信バッファ送信するのアドレス 要素数

各要素の 宛先プロセス メッセージ
データ型 のランク
タグ

コミュニケ―タ

ブロック型受信のパラメータの一般形は次のようになる。

MPI_recv(buf, count, datatype, src, tag, comm, status)
受信バッファ 受信する
各要素の 発信元プロセスメッセージのアドレス 最大要素数 データ型 のランク
タグ

コミュニケ―タ 操作後の状態最大メッセージ長が MPI_Rercv()内に指定されていることに注意しなければならない。
最大長より大きいメッセージが受信されるとオーバーフローエラーが発生し、最大長より短かければメッセージはバッファの前方に格納されて残りは変化しない。しかし、ふつうは大きさがわかっているメッセージが送信されることを期待する。なお、MPI では発信元
あるいは宛先の指定はデータの後に与えられることに注意する必要がある。
【例】整数 x をプロセス０から１へ送信する。
MPI_Comm_rank(MPI_COMM_WORLD,&myrank)/*find process rank */if ( myrank == 0) {int x;MPI_Send(&x, 1, MPI_INT, 1, msgtag, MPI_COMM_WORLD);} else if (myrank == 1) {int x;
MPI_Recv(&x, 1, MPI_INT, 0, msgtag, MPI_COMM_WORLD,status);}
A−６ 非ブロック型ルーチン
非ブロック型ルーチンはただちにリターンする。すなわち、そのルーチンが局所完了であってもなくても次の文の実行を許す。非ブロック型送信 MPI_Isend()（I はただちにという意味の immediate を表す）は発信元内のメモリ位置の変更が安全になる前でもリターンする。非ブロック型受信 MPI_Irecv()は受信するメッセージがない場合でもリターンする。

1020102- 19 -

片山直人

これらのフォーマットは
MPI_Isend(buf, count, datatype, dest, tag, comm, request)
MPI_Irecv(buf, count, datatype, source, tag, comm, request)である。
完了は別のルーチン MPI_Wait()と MPI_Test()によって検出できる。MPI_Wait()
はその操作が実際に完了するまで待ってからリターンし、MPI_Test()は操作がその時点で完了しているかどうかを示すフラグをセットしてただちにリターンする。これらのルーチンは特定の操作が完了したかどうかを知る必要があり、それは request パラメータをアクセスすることでわかる。非ブロック型ルーチンは通信遅延が大きいとき本質的に必要な通信と計算のオーバラップ機能を提供する。
【例】プロセス０からプロセス１へ整数 x を送信し、プロセス０の実行を継続する。
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);/* find process rank */if (myrank == 0) {int x;
MPI_Isend(&x, 1, MPI_INT, 1, msgtag, MPI_COMM_WORLD, req1);
compute();MPI_Wait(req1, staus);} else if (myrank == 1) {int x;
MPI_Recv(&x, 0, MPI_INT, 1, magtag, MPI_COMM_WORLD, status);}
A−７送信モード
MPI の送信ルーチンは、送受信プロトコルを定義する。標準、バッファ付、同期、レデ
ィの４種の送信モードのいずれかをもつ。
標準モードでは、対応する受信ルーチンがスタートしていることを仮定しない。バッフ
ァがある場合のその量は実装依存で MPI には定義されていない。もしバッファされていれば 送 信 は 受 信 に 到 達 す る 前 で も 完 了 で き る 。（ 非 ブ ロ ッ ク 型 な ら 完 了 は 対 応 す る
MPI_Wait()または MPI_Test()がリターンしたときに発生する。）
バッファ付モードでは、送信は対応する受信の前に始まり、リターンするかもしれない。
このモードではアプリケーション・プログラム内にバッファ領域を用意する必要はなく、バッファ領域は MPI ルーチン MPI_Buffer_attach()によってシステムに用意され、また、
MPI_Buffer_detach()によって取り除かれる。
同期モードでは、送受信ルーチンは相手より前にスタートできるが、同時にしか完了しない。

1020102- 20 -

片山直人

レディモードでは、送信は対応する受信に到達してからしかスタートできず、そうでなければエラーとなる。このモードの使用では誤った操作をしないように注意しなければならない。
４種のモードはブロック型および非ブラック型の両方の送信ルーチンに用いることができる。３種の非標準モードは記憶用文字(バッファ付 -b, 同期付 -s, レディ付 -r)で区別され、たとえば、MPI_Issend()は非ブロック型同期送信ルーチンである。ブロック型も非ブロック型も受信ルーチンには標準モードしかなく、対応する送信ルーチンのスタートは仮定されない。どのタイプの送信ルーチンもどのタイプの受信ルーチンとともに使用できる。
A−８集合通信
１対１通信では１個の発信元と１個の宛先プロセスのみが関与するのに対して、ブロードキャストなどの集合通信ではプロセスの集合が関係する。それらのプロセスはイントラ
コミュニケ―タで定義され、メッセージタグは存在しない。
ブロードキャスタおよびスキャタルーチン
MPI はブロードキャストルーチンと各種のギャザーおよびスキャタルーチンを提供する。
コミュニケ―タが集合操作に関与するプロセスの集合を定義し、新しいコミュニケ―タ作
成用に多種のルーチンが用意されている。データに対する主な集合操作は以下の通りである。
MPI_Bcast()

ルートから他の全プロセッサに一斉に同報通信

MPI_Gather()

プロセスのグループから値を収集

MPI_Scatter()

バッファをプロセスのグループに分配

MPI_Alltoall()

全プロセスから全プロセスへデータを転送

MPI_Reduce()

全プロセス上のデータを組み合わせて１個の値をつくる

MPI_Reduce_scatter()

値を組み合わせて結果を分配

MPI_Scan()

プロセス上のデータのプレフィックス簡約を計算関与するプロセスは同じコミュニケ―タ内のものである。各ルーチンにはいくつかの変形がある。
【例】ルートプロセス内に動的に割り付けられたメモリを用いて、プロセス群からプロセス０にデータを集める。
int data[10] ;/*data to be gathered from processes*/.
.

1020102- 21 -片山直人

.
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);/*find rank */if (myrank == 0) {MPI_Comm_size(MPI_COMM_WORLD,&grp_size);buf = (int *)malloc(grp_size*10*sizeof (int));/*find group size*//*allocate memory*/}
MPI_Gather(data, 10, MPI_INT, buf,grp_size*10, MPI_INT, 0,
MPI_COMM_WORLD);MPI_Gather()はルートを含む全プロセスからギャザーを行うことに注意せねばならない。

1020102- 22 -

片山直人