レベル: 中級 Peter Seebach, Freelance author, Plethora.net
2007年 7月 03日 SPE と PPE 間の通信手段のうち、もう 2 つの手段 -- Mailbox とシグナル通知 -- についてみてみましょう。Mailbox は特別な用途のレジスタです。一般的なシステムにおいて周辺デバイスとの通信に使われる I/O レジスタによく似ていていて、SPE と PPE の両方から使用可能です。シグナル通知レジスタは PPE から書き込み・読み出し、どちらも出来ますが、SPE からは読み出しのみ許されています。
前回の記事で、Cell Broadband Engine™ (Cell/B.E.) プロセッサの SPE 単体で動作する最も単純なコードを解説しました。そのコードでは、スレッドの開始時に必要な情報を全て渡し、スレッド終了時に一連のデータを返しました。このプロラミングモデルはある単一のデータブロックを処理する場合には問題ないのですが、複数のデータブロックがある場合、最初に全ての処理コードが SPE にロードさなければならないため、効率が悪いのです。
この記事では、SPE と PPE 間の通信手段として、Mailbox とシグナル通知という、新たに 2 つの手法を紹介します。Mailbox は、特別な用途のレジスタです。一般的なシステムにおいて周辺デバイスとの通信に使われる I/O レジスタによく似ていていて、SPE と PPE の両方から使用可能です。ひとつひとつの SPE は、エントリーをひとつだけ持つことができる 2 つの outbound Mailbox と、4 つのエントリーを持つことができる 1 つの inbound Mailbox の、合計 3 つの Mailbox を持っています。2 つの outbound Mailbox のうち、1 つはインタラプト付き、もう一つはインタラプト無しです。 (今後の実装において、Mailbox のエントリー数は変更される可能性があります。)
シグナル通知レジスタは PPE からは書き込み・読み込み、どちらも可能ですが、SPE からは読み出しのみ許されています。SPE がシグナル通知レジスタから値を読み出しを行うと、値はクリアされます。それぞれの SPE には 2 つのシグナル通知レジスタがあります。
Mailbox は、SPE-PPE 間通信においてより一般的なチャネルインターフェースの特別な場合とみなせるので、は他のチャネルへアクセスする際と同じように、rdch/wrch/rchcnt 命令を使ってアクセスされます。デフォルトでは Mailbox 経由で SPE 達と通信できるのは PPE だけですが、SPE が他の SPE と直接 Mailbox 経由で通信できるようにすることも可能です。一方、シグナル通知レジスタには、誰でも書き込みを行うことができます (訳注: ある SPE の持つシグナル通知レジスタには、PPE からも他の SPE からも書き込みを行うことができる)。各 SPE では、シグナル通知レジスタへのアクセスには特別な命令を使います。これらの機能のすべては PPE 側にある Memory-Mapped I/O (MMIO) レジスタを介してアクセスされます。
通信オプション
数多くある設定項目をどのように設定するかの詳細解説は、この中級者向けの記事の範囲を超えてしまいますので割愛しますが、これらの通信機構には、幅広い設定変更があるということを知っておくのは重要です。例えば、シグナル通知レジスタはインタラプトを発生させるか否かを設定で変更できます。SPE に他の SPE の Mailbox へのアクセス権限を与えることもできます。SPE の inbound Mailbox は、イベントを発生させるか否か (イベントはインタラプトを発生することができます) 設定することができます。SPE はインタラプト付きの Mailbox とインタラプト無しの Mailbox の両方にアクセスすることができます。
つまり、あなたが SPE と PPE との通信に関してやりたいと思ったことは、特別おかしなことでない限りだいたいできると思ってよいでしょう。一般的には、以下のような役割分担が考えられます。
- 大きなサイズのデータについては DMA 転送
- 小さなサイズのデータについては Mailbox
- シグナルを送付したい場合はシグナル通知
注意して欲しいのは、Maibox や他のいくつかのチャネルに対しての SPE 側からのアクセスはブロッキングになるということです。SPE が空のチャネルから読み出しを行ったり、一杯のチャネルに書き込みを行うと、チャネルに変化があるまでストールされてしまいます。このことを利用して、空ループをまわすよりも手軽に消費電力を抑えることもできます。このようなブロッキングの存在を直接変更するオプションはありませんが、rchcnt 命令を使うと、ブロッキングチャネルの中で読み出し可能なものがないか、あるいは、書き込み可能なものがないか、SPE に調べさせることができます。(ちなみに各チャネルは、読み出しか書き出し、どちらかのみとなっています。)
簡単な Mailbox プログラム
はじめに、ごく簡単なプログラムの例を示しましょう。DMA 通信を使わずに、読み出し用 (inbound) の Mailbox から得たデータを書き出し用 (outbound) の Mailbox に書き出すというシンプルな操作をするものです。アルゴリズムではなく API に注目してもらいたいので、一連の操作をインクリメントしておきます。
PPE 側の初期セットアップについては、引数からデータが渡されて計算が始まるという、以前の SPE のサンプルプログラムによく似ています。しかし、今回はデータは Mailbox 経由で独立に渡すことにします。この方法は任意の長さを持ったストリーミングデータをリアルタイムで処理する場合などに応用できます。
上記プログラムを完成するには異なる 2 つの方法があります。SPE の Maibox への書き込みは単純で問題ありません。やや複雑なのは、データを受け取る際です。SPU がデータを返す場合には、2 つの方法があるのです。一つは、インタラプト付きの Mailbox を使って PPE に対してインタラプトをかける方法、もう一つはインタラプト無しの Mailbox を使って、単に後から PPE で読み出す、という方法です。
SPE management library (libspe2) には、インタラプト無しの Mailbox から現在の値を読み出すための spe_out_mbox_read 関数が用意されています。この関数は、一つ以上(設定可能な与えられた上限値)の値を配列に読み出し、読み出した値の数を返り値とします。同様の関数が libspe1 では単一の値しか読み出せず、返り値として読み取った値か -1 を返してきたのに比べると非常に改善されています。また、spe_out_mbox_status 関数をつかって、利用可能な値がキューにいくつたまっているか確認することもできます。もう一つの手法であるインタラプト付きの Mailbox についてですが、こちらはやってきた maibox イベントが受け手のプロセスを起こすまでブロッキングとすることができます。以前のバージョンでは、インタラプトを動作させるために面倒な設定をする必要がありましたが、libspe2 では、それらはユーザーから隠されて、単純化されています。今から示すサンプルプログラムでは、インタラプト付き Mailbox を使いブロッキング読み出しをしています。
The SPE code
SPE のコードは極めてシンプルです。
リスト 1. SPE code to read and write mailboxes
#include <spu_mfcio.h>
int
main(unsigned long long id, unsigned long long argp) {
int i;
while (1) {
i = spu_read_in_mbox();
++i;
spu_write_out_intr_mbox(i);
}
return 0;
}
|
本当に単純ですね。読み出し、書き込みともにブロッキングとなっています。
spu_readch() 関数は読み出し用 (inbound) Mailbox に読み出し可能なデータが入ってくるまでストールとなります。spu_writech() 関数は書き出し用 (outbound) Mailbox が空になるまでストールになります。もし、糖衣構文* が好きでない向きなら、spu_readch() や spu_writech() などのプリミティブ関数を使って直接値の操作をすることもできます。例えば、インタラプト付き Mailbox への書き込み操作は、spu_writech(SPU_WrOutIntrMbox, i); となります。
The PPE code
PPE のコードはもうちょっと面白くなっています。SPE でプロセスを動かし、計算結果を受け取る、という簡単なプログラムとは違って、今回のプログラムはSPEプロセスが動作中に PPE プログラムが動いている必要があります。spe_context_run 関数はブロッキングなので、PPE 側ではスレッドを使います。
"バックグラウンド"で SPE プログラムを動作させるためには、新しいスレッドを作って、そこで SPE プログラムを動かしておけばよいのです。スレッドのメインループは、与えられた SPE コンテキストを動かすだけの、極めて単純なものです。
リスト 2. Running the context
void *
inc_on_spe(void *context) {
spe_context_ptr_t c = context;
unsigned int entry = SPE_DEFAULT_ENTRY;
spe_context_run(c, &entry, 0, NULL, 0, 0);
return NULL;
}
|
これはおなじみの spe_context_run コールで、今回は pthread_create で作られたスレッド内部で動きます。このコードは実際の通信パフォーマンスとしては悪くないですが、ひとつ警告しなければならないことがあります。私は表示上の理由から、エラーチェックのコードを削除しました。エラーチェックを含めた動作するコードは私の手元にあるのですが、そこは是非ご自身で確かめてみて下さい!
リスト 3. PPE communications
context = spe_context_create(0, 0);
spe_program_load(context, &spu_prog);
pthread_create(&inc, NULL, inc_on_spe, context);
i = 0;
while (i < 10) {
int s;
s = spe_in_mbox_write(context, &i, 1, SPE_MBOX_ALL_BLOCKING);
spe_out_intr_mbox_read(context, &i, 1, SPE_MBOX_ALL_BLOCKING);
printf("%d\n", i);
}
|
一度スレッドが開始されれば、ループは単純です。SPE の "in" Mailbox (SPE 側から見た、データ受け取りレジスタです) にデータを書き込み、それからインタラプト付きの Mailbox からデータを読み出します。インタラプト付き Mailbox だけが、ブロッキングの読み出しをサポートしています。
インタラプト無し Mailbox を使ったもう一つの方法では次のようになります。繰り返しになりますが、エラーチェックは行っていないので注意してください。
リスト 4. PPE communications, revised
while (i < 10) {
spe_write_in_mbox(id, i);
while (spe_out_mbox_read(context, &i, 1) < 1)
usleep(100000);
printf("%d\n", i);
}
|
セットアップのためのコードは同じです。このコードでは、ビジーループを避けるために、usleep() を使って読み出し間隔を 100,000 ミリ秒にしています。ただし、もし Mailbox にデータが即時にやってこない場合は、100 ミリ秒の待ちが確定してしまうので、効率が悪いのは確かです。ほとんどの場合よりよい結果となる、インタラプト付きのバージョンを私が最初に示した理由はそこなのです。ちなみに、SPE 側でのコード変更は、spu_write_out_intr_mbox を spu_write_out_mbox に置き換えるだけです。
Real applications
あたりまえの話ですが、通常の状況で単なる一つの 32bit ワードを SPE に送るのに多大なコストをかけたくはありません。Mailbox 通信は、SPE に指示を送り、DMA を使ってデータ転送させるのに便利です。一般的に、Cell/B.E. アーキテクチャでは、PPE ではなく、SPE に DMA 転送させるほうがよい場合が多いです。例えば、もし SPE にプロセスバッファーを持たせているなら、spe_context_run() で設定されたバッファーや、新しく生成したデータブロックのアドレスにどのぐらいのデータ量を送るのか知らせるのに都合がいいでしょう。
シグナル通知
各SPEに 2 つづつあるシグナル通知レジスタは 32bit レジスタですが、Mailbox とは異なり、データを値としてよりはむしろビット列として取り扱います。PPE はシグナル通知レジスタに読み書きできます。SPE は読み出しのみですが、読み出すと同時に値のクリアも行います。それぞれのレジスタは、オーバーライドモード(デフォルトはこちらで、レジスタ書き込む際にデータは上書きされる)か、OR モード (レジスタに書き込む際に、既存のデータと OR 演算された値が書き込まれる) に設定できます。OR モードは複数のソースが SPE に対してなんらかの "シグナル" を送付したい場合などに有用です。
SPE は、シグナルが存在しない場合にシグナルレジスタから値を読み出そうとするとストールしてしまいます。(他のチャネルと同様に、チャネルカウント命令でシグナルの有無を問い合わせできます)シグナルレジスタの読み出しとクリアはアトミック処理でおこなわれます。つまり、どんなフラグがセットされていても、SPE get の直後では、新しくやってきたシグナルはクリア処理が完了するまではセットされません。よって、シグナルを "Lost" してしまうようなことはありません。
SPE 読み出し用 (incoming) Mailbox は、4 つまでのメッセージを保持できるキューですが、シグナルレジスタは単一の値しかもてません。オーバーライドモードでは、単に最新の値を保持するだけですが、OR モードでは、最後に読み出されてからセットされていったビット情報をすべて保持しつづけます。
PPE は、SPE がお互いにシグナルを送付しあえるようにメモリアクセス許可を変更することができます。これにより、OR モードと併用すれば、複数のソースから SPE に対して利用可能なワークロードの通知を行うような多対 1 の通信が可能になります。
「シグナル通知」というフレーズを聞くと、POSIX に習熟したプログラマーならインタラプトを思い浮かべるかもしれませんが、シグナル通知レジスタは必ずしもインタラプトのトリガーとなるものではありません。それらは (読み出し用, 書き出し用 Mailbox も同様に) 定常的なポーリングの換わりに SPE ソフトウェアを反応させるようなイベントを発生するよう設定することもできますが、何のインタラプトも発生させずに使うこともできます。
次回予告: 何で私のスカラーコードはこんなに遅いの?
次の記事では、SPE の SIMD-only アーキテクチャ (スカラー演算器が無く、全ての演算が 16 バイトベクトルで実行される) を紹介し、なぜみなさんのスカラーコードがそんなに遅くなってしまうのか説明します。また、この問題を解決するために開発者が直面する様々な事項について解説し、コンパイラに SPE を効率よく使うコードを生成させるためのコード設計について説明します。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Peter Seebach has a better motto than the US Postal Service: "He always delivers!" That often means "better ways for more effective communication." |
記事の評価
|