第 5 章 Martini 向け
5.5 ソフトウェア実装の通信プリミティブ
5.5.1 メッセージ通信 SEND / RECV
SEND/RECVは送信側と受信側の双方で明示的に送受信関数を呼び出すことでデータのやりと りを行うメッセージ通信型の通信プリミティブである.SENDにより特定プロセスに対するメッ セージの送信を行い,RECVで特定プロセスからのメッセージの受信を行う.
メッセージ通信型の通信モデルでは,実際のデータの転送が抽象化されているため,ユーザは ネットワークの構成を気にせずプログラミングを行うことが可能である.PCクラスタ向けの軽 量通信ライブラリではメッセージ通信型の通信モデルを提供しているものが多く,既存のライブ ラリやアプリケーションの移植を考えた場合,SEND/RECVに対する上位レイヤからの需要は大 きいと考えられる.そこでメッセージ通信型の通信機能をソフトウェアで実装することとした.
SEND/RECVは,上位にMPIなどの通信ライブラリを実装した場合,頻繁に呼び出されるプリミ ティブとなることから,低オーバヘッドで高い通信性能が得られるよう,ハードウェア実装されて いるPUSH・PULLを組み合わせ,極力これらに近い性能が得られるようユーザライブラリ上で実 装した.また,リソースの制限などを考慮し,実際のデータ転送にPUSHを用いたものとPULL を用いたものの2方式を提案・実装した.
データ転送にPUSHを用いたSEND/RECVの実装
PUSHを用いた実装では,SEND要求側がPUSHでRECV要求側にデータを転送し,その上で 別途PUSHを用いてデータを送信したことの通知を行う.RECV要求側には,データ本体を受信 するメッセージバッファ(Message Buffer)と,データのValidや長さなどの個々のメッセージの情 報を保持するディスクリプタテーブル(Descriptor Table)を設ける.メッセージバッファは一定サ イズのブロックに分割して管理し,バッファ管理テーブルやディスクリプタテーブルは分割後し た数と同数のエントリを備える.また,SEND要求側にはリモートのメッセージバッファの使用状 況を管理するバッファ管理テーブル(Buffer Management Table)を設ける.これらバッファやテー ブルのエントリは,それぞれ通信する可能性がある全プロセス分用意する.
図5.2に,PUSHを用いたSNED/RECVの実装において,プロセス0がプロセス1にメッセージ をSENDし,プロセス1がそれをRECVで受信するまでの流れを示す.
Send Buffer
Process 1 Process 0
0 1
n-2 n-1 n 0
1
n-2 n-1 n
0 1 n-2 n-1 n
(2) PUSH (3) Receive Buffer
Copy
Message Buffer (5)
(6) Descriptor Table
(4)Invalidate
PUSH
(1)
Buffer Management Table
図5.2 PUSHを用いて実装したSEND/RECV
まず,SEND要求側は,SENDが呼ばれるとバッファ管理テーブルを参照してRECV要求側の プロセスのメッセージバッファの使用状況を確認する.RECV要求側のメッセージバッファに空 きが存在する場合,バッファ管理テーブルにバッファ使用中を示すフラグを書き(1),PUSHを用 いて送信データが置かれている領域(図中のSend Buffer)からRECV要求側のメッセージバッファ の空き領域へ直接データを書き込む(2).その後,続けて送信したメッセージに関する情報をディ スクリプタテーブルに対してPUSHを用いて書き込む(3).ディスクリプタテーブルは,メッセー ジバッファの分割数と同じ数のブロックに分割されており,送信したデータ本体の先頭が書かれ たメッセージバッファのブロックに対応するエントリにのみ,メッセージ情報が書き込まれる.
RECV要求側は,RECV関数が呼ばれると,送信元のプロセスに対応するディスクリプタテーブ ルをポーリングし,メッセージの到着を確認する.ディスクリプタテーブルに有効なメッセージ情 報が書き込まれていることを確認し,メッセージの情報を読み取ったら,メッセージバッファから 受信メッセージをコピーするなどの処理を行い(4),メッセージバッファの受信メッセージが格納 されていた領域に対応するディスクリプタテーブルのエントリを無効化する(5).最後に,SEND 要求側のバッファ管理テーブル内のバッファの有効性を示すフラグを,PUSHを用いて消去する
(6).
データ転送にPULLを用いたSEND/RECVの実装
データ転送にPULLを用いた実装では,SEND要求側は送信データを用意してRECV要求側に データの所在のみをPUSHで通知し,RECV要求側は送信データをPULLでSEND要求側から読 み出すことで取り込む.そのため,RECV要求側にSEND要求側で用意した送信データの所在に 関する情報を受信するためのディスクリプタテーブル(Descriptor Table)と,PULLでデータを取 り込むのに用いる受信バッファ(Receive Buffer)を設ける.また,SEND側には,RECV側の受信 キューの空き情報を管理する受信管理テーブル(Receive Management Table)と,PULLが完了する まで送信データを保持するのに用いる送信バッファ(Send Buffer)を設ける.
図5.3にPULLを用いて実装したSEND/RECVにおいて,プロセス0がプロセス1にメッセー ジをSENDし,プロセス1がそれをRECVで受信するまでの流れを示す.
Send Buffer
Process 0 0
1
n-2 n-1 n 0
1
n-2 n-1 n
(2) Receive Buffer
(4)
(5) Descriptor Table
(3)Invalidate
PUSH
PULL
Process 1
(1)
Receive Management Table
図5.3 PULLを用いて実装したSEND/RECV
SEND要求側は受信管理テーブルを通じてリモートのディスクリプタテーブルに空きがあるこ とを確認したら,受信管理テーブルの空き領域にディスクリプタテーブルのエントリが使用中で あることを示すフラグを書き込み(1),フラグを書き込んだ部分に対応するRECV側のディスク リプタテーブルに対して送信バッファに置いたメッセージの情報をPUSHで書き込む(2).
RECV要求側は,RECV関数が呼ばれると,メッセージの到着を確認するためにディスクリプ タテーブルをポーリングする.メッセージの到着が確認できたら,ディスクリプタテーブルの中身 を無効化し(3),ディスクリプタテーブルに書かれているSEND要求側の送信バッファから,ロー カルの受信バッファにPULLでデータを読み出す(4).その後,SEND要求側の受信管理テーブル 内のフラグをPUSHでクリアする(5).SEND要求側は,受信管理テーブルのエントリを確認して 送信バッファが再利用可能かどうかを判断する.
2種類のSEND/RECVの実装の比較
PUSHを用いた実装では,メッセージバッファを通信相手となるプロセス数分用意しなければ ならないため,メモリの消費量が大きくなりやすいという問題点がある.これに対し,PULLを 用いた実装では,受信バッファを相手プロセス数分用意する必要がなく,リソースの消費を抑え られるが,一方でPUSHによる確認が行われた上でPULLによるデータ転送が行われるため,サ イズの小さいメッセージの場合,レイテンシの影響が大きくなると考えられる.
そこで,PUSHを用いた実装は中程度のサイズのメッセージ用に,PULLを用いた実装はサイ ズの大きなメッセージ用に,それぞれを使い分けることで,双方の問題を解決する.また,PUSH を用いた実装においても,サイズの小さいメッセージについては,メッセージを直接受信キュー に書き込む実装とすることでより低レイテンシな通信を実現する.