CTKによるオーバレイタスク
出典: CTK: Cell ToolKit Library
「CTKユーザマニュアル」に戻る
- 「CTKによるCプログラミング」では1つのSPEプログラムをSPEスレッドやSPEコンテキストとして直接1つの論理的なSPEに結びつけて実行してきました。このページでは、SPEスレッド上でオーバレイして実行されるSPEタスクとしてプログラムを実行する方法について説明します。
目次 |
[編集]
SPEオーバレイタスクとは
- SPEタスクはSPEスレッドと良く似たSPEの実行体ですが、SPEスレッドの上でアプリケーションレベルで実現されているオーバレイタスクです。オーバレイタスクとは、実行プログラムの一部を他のコードと置き換えながら実行する手法です。
- 補足:Cell SDK 2.X以降の環境では、リンカを使ってオーバレイを実現することが可能です。リンカを使ったオーバレイとCTKのようなライブラリによるオーバレイは、実現方法の違いからできることにいくつか違いがあります。最も大きな違いは、ライブラリを使った場合はオーバレイされるコードが実行時に決定されるのに対し、リンカを使った場合はリンク時に決定されるという点です。
- SPEスレッドは生成してから実行を開始するのにmsec単位のオーバヘッドがかかりますが、SPEタスクはずっと軽量に実行できます(実行に数usec程度)。また、複数のSPEをタスク用にセットアップしておいて空きSPEの上で次々とタスクを実行させることができるため、SPE資源を有効に使うプログラムを比較的簡単に書くことができます。
- SPEタスクの中から新しいSPEタスクを生成したり、他のSPEタスクと同期を取ったりすることが可能です。ある1まとまりのことをするプログラムを協調動作する複数のSPEタスクとして記述することで、SPEセントリックなプログラムを書くことができます。
- なお、CTKのSPEタスクは、基本的にはテキスト開始位置をランタイム以降の固定アドレス (0x3000から) とした通常のSPE実行プログラムです。このページでは ctk-tool を使ってSPEタスクを作る方法を説明していますが、ツールを使わなくてもspu-gccに-Ttext 0x3000を指定してリンクすればSPEタスクのバイナリを作ることができます。
[編集]
SPEオーバレイタスクを使ったプログラム
- SPEタスクを使って各タスクから"Hello, World"と出力するだけのプログラムは以下のようになります。
PPEソースコード
#include <ctk.h>
#include <pthread.h>
#include "spe-hello-embed.h"
#define NSPES 6
#define NTASKS 16
int main() {
int i;
ctk_spe_runtime_t runtime;
ctk_spe_task_t task[NTASKS];
ctk_spe_task_image_t image;
/* タスクランタイムの生成 */
ctk_spe_runtime_create(&runtime, NSPES, 0, 0, 0);
/* タスクイメージとタスクの生成 */
ctk_spe_task_image_create(&image, &spe_hello);
for (i = 0; i < NTASKS; i++)
ctk_spe_task_create_by_image(&task[i], image, 0);
/* タスクの実行開始 (実行待ちキューに入れる) */
for (i = 0; i < NTASKS; i++)
ctk_spe_runtime_enq_task(runtime, task[i], NULL, NULL);
/* 全タスクの終了を待つ */
ctk_spe_runtime_wait_all(runtime, CTK_SPE_TASK_MASK_ALL);
/* タスクの破棄 */
for (i = 0; i < NTASKS; i++)
ctk_spe_task_destroy(task[i]);
/* ランタイムとタスクイメージの破棄 */
ctk_spe_task_image_destroy(image);
ctk_spe_runtime_shutdown(runtime);
return 0;
}
- このプログラムでは、SPEを6個使って、その上で16個の"Hello, World" SPEタスクを次々と実行しています。
- プログラムの骨格は大体次のようになります。
- ctk_spe_runtime_create関数でSPEタスクを実行する環境となるSPEランタイムを生成します。第2引数で指定された数(ここでは6個)のSPEがSPEタスクによって使われることになります。
- ctk_spe_task_image_create関数でプログラムイメージからタスクのためのSPEタスクイメージを生成します。
- ctk_spe_task_create_by_image関数でSPEタスクイメージからSPEタスクを生成します。(なお、ややメモリ効率が悪くなりますが、SPEプログラムイメージから直接SPEタスクを生成するctk_spe_task_createという関数もあります)
- ctk_spe_runtime_enq_task関数で生成されたSPEタスクの実行を開始します。関数の第3引数と第4引数にはSPEタスクのmain関数ctk_task_main(後述)に渡される引数を2つ指定できます。この関数は、指定されたSPEタスクの実行を「すぐに」開始するわけではなく、ランタイムの実行待ちキューにタスクをエンキューします。実行待ちキューにエンキューされたタスクは、空きSPEがある限り次々と非同期に実行されます。
- ctk_spe_runtime_wait_all関数で実行待ちキューに入れた「すべての」タスクの実行終了を待ちます。この他に、個々のタスクの実行終了を待つctk_spe_runtime_wait_task関数や、指定されたタスクのうち「どれか1つ」の実行終了を待つctk_spe_runtime_wait_any関数が用意されています。
- 実行が終わったら、ctk_spe_task_destroy関数でSPEタスクを、ctk_spe_task_image_destroy関数でSPEタスクイメージを、ctk_spe_runtime_shutdown関数でSPEランタイムをそれぞれ破棄しています。
SPEソースコード
- SPEタスクのソースコードは次のようになります。
#include <ctk_spu_task.h>
int ctk_task_main(unsigned long long taskid, unsigned long long argp,
unsigned long long envp) {
printf("[SPE Task] Hello, World!\n", taskid);
}
- ctk_spu.hファイルの代わりにctk_spu_task.hファイルを、main関数の代わりにctk_task_main関数を指定しているほかは、普通のSPEスレッドのソースコードと殆ど変わりません。実際に、SPEスレッド内で使えるほとんどのAPIをSPEタスク内でも利用可能です。
- SPEタスクをビルドするには、ctk-toolビルドツールを次のように実行します。
$ <installdir>/bin/ctk-tool spu-task-build --output=spe-hello spe-hello.c
- また、SPEタスクをCESOF形式のオブジェクトとしてビルドするには、ctk-toolビルドツールを次のように実行します。
$ <installdir>/bin/ctk-tool cesof-task-build --output=spe-hello-embed.o spe-hello.c
- 今回の例では、PPEプログラムのソースコードではCESOF形式のオブジェクトとリンクすることを仮定しているので、後者の方法でビルドします。
- 実行結果は次のようになります。
$ ./ppe-hello [SPE Task] Hello, World! [SPE Task] Hello, World! [SPE Task] Hello, World! [SPE Task] Hello, World! [SPE Task] Hello, World! [SPE Task] Hello, World! ... (16回繰り返し)
- なお、今回の例では使っていませんが、SPEタスクのmain関数(ctk_task_main関数)の返り値は、タスク実行終了後であればctk_spe_task_get_exit_code関数を使って取得することができます。
| ctk_spe_runtime_create | SPEランタイムを生成する |
| ctk_spe_task_image_create | SPEプログラムイメージからSPEタスクイメージを生成する |
| ctk_spe_task_create_by_image | SPEタスクイメージからSPEタスクを生成する |
| ctk_spe_task_create | SPEプログラムイメージから直接SPEタスクを生成する |
| ctk_spe_runtime_enq_task | SPEタスクの実行を開始する |
| ctk_spe_runtime_wait_all | すべてのタスクの実行終了を待つ |
| ctk_spe_runtime_wait_task | 個々のタスクの実行終了を待つ |
| ctk_spe_runtime_wait_any | 任意のタスクのの実行終了を待つ |
| ctk_spe_task_destroy | SPEタスクを破棄する |
| ctk_spe_task_image_destroy | SPEタスクイメージを破棄する |
| ctk_spe_runtime_shutdown | SPEランタイムを破棄する |
[編集]
SPEタスクから他のSPEタスクを実行する
- SPEタスク自身から他のSPEタスクを実行する方法には、大きく分けて2種類の方法があります。1つはctk_task_spawnというAPIを使う方法で、もう1つはctk_task_execというAPIを使う方法です。これらのAPIはどちらもSPEタスクからしか使うことが出来ません。
[編集]
ctk_task_spawnによる新しいSPEタスクのエンキュー
- SPEタスクは、PPEからだけではなくSPEタスク自身からも実行待ちキューにエンキューすることが可能です。SPEタスクから新しいSPEタスクを実行待ちキューに入れるには、ctk_task_spawnというAPIを使います。
- ctk_task_spawnは、SPEタスク(ctk_task_createの第1引数で返される値)とSPEタスクの実行時に渡される引数2つを引数に取ります。SPEタスク自体はあらかじめPPE上でctk_task_createによって生成されている必要があることに注意してください。
[編集]
ctk_task_execによる新しいSPEタスクイメージの実行
- 新しいタスクを実行待ちキューに入れる代わりに、今現在実行していたタスクの仕事をやめて新しいタスクイメージで自分自身を置き換えるAPIも用意されています。SPEタスク自身のイメージを新しいイメージで置き換えるには、ctk_task_execというAPIを使います。
- ctk_task_execは、SPEタスクイメージ(ctk_task_image_createの第1引数で返される値)とSPEタスクの実行時に渡される引数2つを引数に取ります。SPEタスク自体はあらかじめPPE上でctk_task_image_createによって生成されている必要があることに注意してください。
[編集]
SPEタスクから使うことのできる主なタスク制御API
| ctk_task_yield | 実行を一時停止して、別のSPEタスクにSPEプロセッサ資源を譲る |
|---|---|
| ctk_task_exec | 現在のタスクに新しいタスクイメージを読み込んで、別のタスクとして実行を開始する |
| ctk_task_spawn | 新しいSPEタスクを実行開始する (実行待ちキューに入れる) |
| ctk_task_exit | SPEタスクを終了する |
[編集]
他のSPEタスクと待ち合わせながら仕事をする
- CTKのSPEタスクの大きな特徴は、複数のSPEタスクでSPE資源を効率的に使うことができるという点です。例えば、SPEタスク間で同期を取る場合に、ctk_mutex_trylockが失敗したら一時的にSPE資源を他のSPEタスクに譲る、というプログラムを簡単に(かつ軽量に)記述することができます。
SPE資源を譲り合いながら同期するSPEタスクのコード例
#include <ctk_spu_task.h>
int ctk_task_main(unsigned long long taskid, unsigned long long argp,
unsigned long long envp)
{
...
while (ctk_mutex_trylock(lock) != CTK_ERROR_SUCCESS)
ctk_task_yield(); /* ロックに失敗したら他のタスクにSPEを譲る */
...
}
- いくつかの同期APIには、SPEタスク間で資源を調停しながら同期待ちを行うためのAPIがあらかじめ用意されています。
| ctk_task_barrier_wait | SPEタスク同士でバリア同期を取る |
| ctk_task_queue_enq | SPEタスク同士で調停しながらキューにアイテムを入れる (一杯だったら他のSPEタスクに一時的にSPE資源を譲る) |
| ctk_task_queue_deq | SPEタスク同士で調停しながらキューからアイテムを取り出す (空だったら他のSPEタスクに一時的にSPE資源を譲る) |
「CTKユーザマニュアル」に戻る
