通信ベンチマーク記述言語 coNCePTuaL

coNCePTuaLという、なかなか使えるツールを発見したので、関連論文を読んだり、実際に使ってみた。
coNCePTuaL
Paper of coNCePTuaL

coNCePTuaLとは

coNCePTuaLは、複数のプロセス間で通信を行うベンチマークアプリケーションを容易に記述するための言語と、その言語からバイナリに変換するためのコンパイラからなる。通信ライブラリ実装者がライブラリの性能テストを容易に行えるよう、あるいは、並列アプリケーション実装者がアプリケーション内で行う通信が高速に行えるか事前にテストできるように、研究・開発されたツールである。
coNCePTuaL言語からバイナリにコンパイルする際に使用する(中間)言語や通信ライブラリのことをバックエンドといい、これを目的に応じて入れ替えることができる。たとえば、言語としてC言語、ライブラリとしてSocketを用いてUDPで通信を行うベンチマークプログラムを生成したい場合にはc_udgramバックエンドを指定してコンパイルすればよい。また、通信ライブラリとしてMPIを用いたバイナリを生成したい場合にはc_mpiバックエンドを指定してコンパイルするだけで生成できる。さらに便利なことに、latex_visというバックエンドがあり、これを指定してコンパイルすると、coNCePTuaLコードで記述された通信パターンを時系列グラフとしてEPS形式で出力してくれる。論文に通信パターンの図を載せる場合に便利。また、バックエンドを実装するためのpython APIも提供されている。
coNCePTuaLを使ってうれしいのは、バックエンドの言語・ライブラリを使った場合に比べ、コード量が大幅に減ること。C言語でMPIを使う場合には通信用のコード以外にも初期化、終了処理やメモリ管理も行わなければならないが、coNCePTuaLの場合は一切記述する必要がない。簡単な通信パターンであれば1行だけで実装できる。

coNCePTuaLサンプルコード

先頭プロセスから最終プロセスまでプロセスIDの順にメッセージをリレーする通信コードは以下のように2行でかける。

msgsize is "Message size (bytes)" and comes from "--msgsize" or "-m" with default 0.

All tasks src send a msgsize-byte message to task dst such that dst = src+1

最初の1行目は、生成されるベンチマークプログラムへの引数のルールを指定している。「--msgsize」や「-m」オプションでリレーされるメッセージサイズを指定できる。
2行目が通信のためのコード。coNCePTuaLではtaskが1つのプロセスを表す。このコードは以下を意味する。

  • 「src」と言うIDを持つすべてのタスクが「msgsize」バイト分のメッセージを「dst」タスクに送信する
  • そのときの条件として「dst」は「src + 1」、すなわちタスクID空間で隣接したタスクに送信する

すごく英語的な文法。
変数msgsizeはプログラムの引数から取得する。デフォルトでは1行目に書かれているように0となる。つまり、メッセージサイズを決めうちにしてしまえば、実質1行で書けるわけだ。src、dst変数は自動で生成される。
send命令を記述すると、宛先プロセスでは暗黙的にreceive命令が実行される仕様になっているので、このように簡単に記述できる。なお、送受信にはブロッキング、ノンブロッキング通信両方がサポートされている。このサンプルではデフォルトの動作であるブロッキング通信が使用される。受信は送信に先立って実行されるため、このサンプルではタスク0からタスク1にメッセージを送信し、タスク1は受信後にタスク2に送信する、という具合にリレー通信が行われる。
このコードをchain.ncptlと言う名前で保存した場合、次のようにコンパイルできる。

$ ncptl --backend=c_mpi chain.ncptl

chainという名のmpiバイナリが生成されるので、通常通りmpich2を用いて実行できる。

$ mpd&
$ mpiexec -n 8 ./chain --msgsize=4096

実際はこれだけだと1回の通信しか行わず、さらに通信時間の通知も行わないので、次のように繰り返し処理を行い、その平均値を計算し、結果をファイルに出力するように実装する必要がある。

msgsize is "Message size (bytes)" and comes from "--msgsize" or "-m" with default 0.

for 1000 repetitions {
  task 0 resets its counters then
  all tasks src send a msgsize-byte message to task dst such that dst = src+1 then
  task num_tasks-1 sends a 0-byte message to task 0 then
  task 0 logs the mean of elapsed_usecs as "Average time (usecs)"
          and the standard deviation of elapsed_usecs as "Standard deviation (usecs)"
}

リレーが終わった後で、最後のタスク(task num_tasks-1)からタスク0に完了を通知している。この2つの通信命令の前後に時刻記録用の処理を追加した。タスクごとに経過時間などのカウンターを持っており、繰り返しのたびに時刻を0にセットしている。通信終了後には「elapsed_usecs」で通信にかかった時間を取得し、その平均と標準偏差を記録する。ちょっとコードが長くなったけど、それでもC言語+MPIで実装するよりもはるかに短い。また、ログファイルには通信にかかった時間だけでなく、使用した計算機環境についてのハードウェアとソフトウェアの詳細な情報も出力される。
1つおもしろいことに、C言語なら「;」で文と文を区切るところを、coNCePTuaLでは「then」で区切り、次の文につなげている。これも英語的。
coNCePTuaLコードから通信パターングラフを作成する場合には次のようにコンパイルすればよい。

$ ncptl --backend=latex_vis --filter='s/sep=30bp/sep=20bp/g' chain.ncptl --tasks=8

こんな感じの図が書ける。

coNCePTuaLのインストール

最後にインストール方法について。Ubuntu 8.04でインストールと動作を確認済み。
まず、coNCePTuaL本体コンパイルに必要なライブラリをインストール。latex_visを使うのであればlatexの環境一式が必要になる。latex_visを使わないのであれば、python-devだけでOK。

$ sudo aptitude install python-dev
$ sudo aptitude install texlive-latex3
$ sudo aptitude install texlive-pstricks
$ sudo aptitude install texlive-latex-recommended
$ sudo aptitude install texlive-fonts-recommended

coNCePTuaLのコンパイルは通常通りconfigure、make、make install。

$ tar zxf conceptual-0.8.1.tar.gz
$ cd conceptual-0.8.1
$ ./configure --prefix=/usr/local/stow/conceptual-0.8.1
$ make
$ make install
$ cd /usr/local/stow
$ sudo stow -t /usr/local conceptual-0.8.1
$ cd /etc/ld.so.conf.d
$ sudo ldconfig

僕はソースからコンパイルしたソフトウェアはすべてstowで管理しているので7行目にstowコマンドが入っている。また、最終的には/usr/local/lib以下にcoNCePTuaLのライブラリがインストールされるので、ldconfigでライブラリキャッシュを再構成しておく必要がある。

まとめ

僕の研究では、MPIアプリケーションの通信パターンを取得し解析している。アプリケーションのソースコードや論文から通信パターンを抽出して通信パターンのみcoNCePTuaLで実装すれば、わざわざ長時間をかけてアプリケーションを実行しなくても十分評価が行える。実際、coNCePTuaLの論文や、ソフトウェアには実アプリケーションの通信パターンを実装したコードが付属しているので、それらを使用するだけでも十分評価の幅が広がる。まさに求めていたツールである。