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タスクを次々と実行しています。
  • プログラムの骨格は大体次のようになります。
  1. ctk_spe_runtime_create関数でSPEタスクを実行する環境となるSPEランタイムを生成します。第2引数で指定された数(ここでは6個)のSPEがSPEタスクによって使われることになります。
  2. ctk_spe_task_image_create関数でプログラムイメージからタスクのためのSPEタスクイメージを生成します。
  3. ctk_spe_task_create_by_image関数でSPEタスクイメージからSPEタスクを生成します。(なお、ややメモリ効率が悪くなりますが、SPEプログラムイメージから直接SPEタスクを生成するctk_spe_task_createという関数もあります)
  4. ctk_spe_runtime_enq_task関数で生成されたSPEタスクの実行を開始します。関数の第3引数と第4引数にはSPEタスクのmain関数ctk_task_main(後述)に渡される引数を2つ指定できます。この関数は、指定されたSPEタスクの実行を「すぐに」開始するわけではなく、ランタイムの実行待ちキューにタスクをエンキューします。実行待ちキューにエンキューされたタスクは、空きSPEがある限り次々と非同期に実行されます。
  5. ctk_spe_runtime_wait_all関数で実行待ちキューに入れた「すべての」タスクの実行終了を待ちます。この他に、個々のタスクの実行終了を待つctk_spe_runtime_wait_task関数や、指定されたタスクのうち「どれか1つ」の実行終了を待つctk_spe_runtime_wait_any関数が用意されています。
  6. 実行が終わったら、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関数を使って取得することができます。


PPEプログラムでSPEオーバレイタスクを使うための主なAPI
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

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があらかじめ用意されています。
SPEタスク間で使える主な同期API
ctk_task_barrier_wait SPEタスク同士でバリア同期を取る
ctk_task_queue_enq SPEタスク同士で調停しながらキューにアイテムを入れる (一杯だったら他のSPEタスクに一時的にSPE資源を譲る)
ctk_task_queue_deq SPEタスク同士で調停しながらキューからアイテムを取り出す (空だったら他のSPEタスクに一時的にSPE資源を譲る)

CTKユーザマニュアル」に戻る

表示