Cell/B.E.用のソフトウェア開発キット (Software Development Kit, SDK) にはツールチェーン、ライブラリ、サンプルコード、フレームワークや関連資料が含まれています。フレームワークの一つであるAccelerated Library Framework (ALF) の新しいタスクモデルである、軽量タスクサポート (Lightweight task support, LTS) - ALF LTS と略します - は、SDK3.1から加わりました。ALF LTSは、Cell/B.E. のヘテロジニアスマルチコア環境で必要な様々な定型コード(複数のSPEコアの管理に関するコードなど)を書かずにすむ、多数の機能を提供する便利なフレームワークです。今回は、このフレームワークを使った基本的なプログラム作成のステップについて解説します。ではまず おさらいとして、プログラミングの際に意識することが必要なCell/B.E.プロセッサーの構成から見ていきましょう。
Cell/B.E.は2種類の9個のコアからなるヘテロジニアス(異種混合)マルチコアプロセッサーです。プログラミングにおいては、この2種類のコアの特徴やプロセッサー全体のアーキテクチャーを知ったうえで、その長所を発揮できるようなプログラム構成を考える必要があります。コアの種類や特徴について以下の表に示します。
表1. Cell/B.E.を構成する2種類のコア
| コア名 | 個数 | 概要 | 特徴 |
|---|---|---|---|
| PowerPC Processor Element (PPE) | 1 | PowerPC汎用プロセッサー互換のコア | OSを駆動し、SPEの制御を行う。32KBのL1キャッシュと512KBのL2キャッシュを持ち、PPEだけでPowerPC互換のプログラムを実行することが可能。 |
| Synergistic Processor Element (SPE) | 8 | 浮動小数点演算に特化したコア | 演算を担うSynergistic Processor Unit (SPU) と256KBの専用メモリLocal Store (LS) とから構成される。ベクトル型演算器により、高速演算を行う。SPUが使用するプログラムやデータはDirect Memory Access (DMA) を用いてメインメモリからLSに明示的にロードする必要がある。また、処理結果のデータもDMAを用いてLSからメインメモリに明示的に転送する必要がある。 |
また、Cell/B.E.の構成を以下の図に示します。
図1. Cell/B.E.の構成
コア以外の説明は以下のようになります。 図1に示すように、各々非常に高速なバンド幅を提供します。
- EIB (Element Interconnect Bus) : 9個のコアや下記コントローラーを接続するリング状内部バス。
- MIC (Memory Interface Controller : デュアルチャネルに対応するメモリーコントローラー。
- BIC (Bus Interface Controller : 周辺デバイスと接続するためのバスコントローラー。
このようにCell/B.E.は2種類のコアから構成されており、この構成に対応したプログラム作成が必要です。さらに、最大のパフォーマンスを引き出すためには、コアの特性を意識し、最適化したプログラム作成が必要となります。今回は前者の"コアの構成に対応したプログラム作成"について解説し、次回以降で後者の"コアの特性を意識し、最適化したプログラム作成"について解説します。
では、"コアの構成に対応したプログラム作成"について解説していきましょう。Cell/B.E.内でプログラムが動くイメージを図2に示します。
図2. Cell/B.E.のプログラム動作のイメージ
Cell/B.E.では8個のSPU上で個々にプログラムを実行させます。(SPUは高速な浮動小数点演算処理が得意なため、そのような演算処理をSPUで主に実行させるようにプログラムしますが、これについても次回以降で解説します。) SPUのプログラムを起動するのはPPUで、スレッドとして個々に起動します。SPUは1つのCell/B.E.プロセッサーの中に8個ありますので、SPU側では8個のスレッドが同時に動作可能ということになりますね。
一方、PPUはSPUの制御を行うという使命がありますが、それだけではありません。PowerPC互換のプログラムを実行したり、SPUの苦手な処理を担当したり、並列演算処理を実行することもできます。PPUは1つのCell/B.E.プロセッサーの中に1個だけですが、1つで2個のスレッドが動作します。したがって、トータルとして、1つのCell/B.E.プロセッサーで同時に10個のスレッドを起動することができます。
通常、コアごと(PPU用、SPU用)にプログラムを作成し、PPUから複数のSPUへのプログラムのロードや、スレッドを起動・管理するコードなど、多数の処理コードを作りこむ必要がありますが、この作業を簡潔にする方法が以下のように大きく分けて2種類あります。
- シングルソースコンパイラを使って1つのプログラムで構成する方法 -- XLC SSC(Single Source Compiler)、CellSs(Cell Superscalar)などを利用
- フレームワークを利用してプログラミングを簡易化する方法 -- ALF、ALF LTS、OpenCL などを利用
ここでは後者の方法のうち ALF LTS を利用したプログラミングについて解説します。
※ XLC SSCの詳細についてはXLC で行こう!: 第 1 回 Cell/B.E.用 XLC は2種類あるよをご覧ください。
※ CellSsの詳細についてはCell Superscalar はどうだい?: 第 1 回 まずは使ってみようをご覧ください。
ALF LTSは SDK3.1以降に含まれるフレームワークで、以下のような機能が含まれます。この機能を利用することによって、プログラムを簡潔に作成することができます。たとえば30行ほどの定型コードだけでPPUプログラムを簡潔に作成することができます。
ALF LTSの機能:
- 複数SPUの管理
- SPUプログラムの実行時読み込み
- SPUプログラムの切り替え
- タスク依存関係の設定
※ SDK3.1の詳細についてはSDK 解説: SDK バージョン 3.1 の紹介をご覧ください。
※ ALF LTSの詳細についてはLet's LTS: 第 1 回 ALF 軽量タスクサポートの概要をご覧ください。
Hello World - ALF LTS版 サンプルコードをダウンロードしてプログラムをご覧ください。この中で4種類の"Hello World"プログラムを紹介しています。プログラムの構成は図1のようになり、(1)から(4)で示すディレクトリーごとに異なる4種類の"Hello World"プログラムを含んでいます。
図3. 4種類のHello World プログラムの構成
(1)から(4)の4種類の"Hello World"プログラムの内容は以下のとおりです。
(1) STEP0_hello_ppu : PPUだけを使用して"Hello World"を表示する、PowerPC互換のプログラムです。
(2) STEP1_hello_spu : SPU 1個を使用して"Hello World"を表示するプログラムです。SPUを使用する場合は、通常PPUとSPU両方のプログラミングが必要ですが、ここではSPUだけのプログラムで実行を可能としています。SPUletというプログラミングモデルです。(PPUのプログラムはありませんが、実行時は、通常のプログラムと同様に、SPUの管理はPPUが行っています。)
(3) STEP2_hello_ppu+spu : SPU 1個を使用して"Hello World"を表示するプログラムです。PPUとSPUの両方のプログラミングを必要とするモデルです。PPUのプログラムは"STEP2_hello_ppu+spu"ディレクトリーの直下に、SPUのプログラムは"spu"ディレクトリーの中に含まれます。
(4) STEP3_hello_ppu+8spu : SPU 8個それぞれが"Hello World"を表示するプログラムです。(3)との違いは使用するSPUの個数です。SPUのプログラムは(3)のSPUプログラムを使用しています。((3)の"spu"ディレクトリー以下をSPUプログラムとして使用するよう、(4)のMakefileに記述しています。)使用するSPUの個数はPPUプログラムの中で定義しています。
※ 実際のプログラムの開発ステップは (1)→(3)→(4)のように進めることが多いと考えてよいでしょう。(2)のプログラミングモデルは簡単にSPU単体の動作確認を行いたい場合に便利な手法です。
では、(1)から(4)のコードの内容について以下で詳細に説明します。
以下に(1)のコードを示します。
リスト1. main.c
001: #include <stdio.h> // for printf()
002:
003: int main(int argc, char *argv)
004: {
005: printf("Hello PPU !!\n");
006:
007: return 0;
008: }
|
PPUを使って"Hello PPU!!"と画面に表示するプログラムです。ここではSPUは使いません。
リスト2. Makefile
001: PROGRAM_ppu = hello_ppu 002: 003: include $(CELL_TOP)/buildutils/make.footer |
Makefileの001行目でプログラムの実行ファイル名を指定しています。003行目では、make.footer という擬似ビルド環境を有効にする定義ファイルを読み込んでいます。ここで CELL_TOPは SDKのあるディレクトリ /opt/cell/sdk/ を示します。環境変数として以下を一旦実行して指定しておけば、003行目のように記述することができます。
export CELL_TOP=/opt/cell/sdk/
※ 擬似ビルド環境 および make.footerファイルについての詳細はクローズアップ SDK 3.1: 第 2 回 make.footer を使おうをご覧ください。
次に(2)のコードを示します。
リスト3. main_spu.c
001: #include <stdio.h> // for printf()
002:
003: int main(unsigned long long speid __attribute__((unused)),
004: unsigned long long parm __attribute__((unused)))
005: {
006: printf("Hello SPU !!\n");
007:
008: return 0;
009: }
|
SPUを使って"Hello SPU!!"と画面に表示するプログラムです。コマンドプロンプトから直接SPU実行ファイル名を入力してプログラムを実行することができます。このプログラムモデルでは、PPUのプログラムを準備する必要はありませんが、ランタイム環境により、SPUへのプログラムのロードやPPUからのSPU制御が行われます。このプログラミングモデルをSPUletと呼んでいます。簡単にSPU単体の動作確認を行いたい場合に便利な手法です。
リスト4. Makefile
001: PROGRAM_spu = hello_spu 002: LDFLAGS_gcc = -mstdmain 003: include $(CELL_TOP)/buildutils/make.footer |
002行目はコンパイルオプションで、スタートアップコードをリンクします。このMakefileを使ってビルドすると、SPU実行ファイル"hello_spu"を作成し、これを直接コマンドプロンプトに入力してSPUのプログラムを起動することができます。
次にALF LTSを利用したプログラムをご紹介します。1個のSPUを使って"Hello World!!"を画面に表示します。PPU側のコードを以下に示します。
リスト5. main.c
001: #include <stdio.h> // for NULL
002: #include <alf.h> // for alf_xxxx()
003:
004: int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
005: {
006: alf_handle_t handle;
007: alf_task_desc_handle_t task_desc_handle;
008: alf_task_handle_t task_handle;
009:
010: alf_init(NULL, &handle); // ALFの初期化 (ALFハンドルの作成)
011: alf_num_instances_set(handle, 1); // ALFの設定:ALFハンドルと使用するSPUの個数(ここでは"1")の設定
012:
013: alf_task_desc_create(handle, 0, &task_desc_handle); // タスクの内容を記述するタスクデスクリプタを作成
014: alf_task_desc_set_int32(task_desc_handle,
015: ALF_TASK_DESC_TASK_TYPE,
016: ALF_TASK_TYPE_LIGHTWEIGHT); // タスクタイプを指定
017: alf_task_desc_set_int64(task_desc_handle,
018: ALF_TASK_DESC_ACCEL_LIBRARY_REF_L,
019: (unsigned long long) "libhello_spu.so"); // SPUプログラムが格納されている共有ライブラリ名を指定
020: alf_task_desc_set_int64(task_desc_handle,
021: ALF_TASK_DESC_ACCEL_IMAGE_REF_L,
022: (unsigned long long) "hello_spu"); // 実行ファイル名を指定
023: alf_task_desc_set_int64(task_desc_handle,
024: ALF_TASK_DESC_ACCEL_LTS_MAIN_REF_L,
025: (unsigned long long) "task_main"); // メイン関数の名前を指定
026:
027: alf_task_create(task_desc_handle, NULL, 1, 0, 0, &task_handle); // タスクの作成
028: alf_task_finalize(task_handle); // タスクの実行
029:
030: alf_task_wait(task_handle, -1); // タスク完了待ち
031:
032: alf_exit(handle, ALF_EXIT_POLICY_FORCE, 0); // ALFの終了 (リソースを開放)
033:
034: return 0;
035: }
|
プログラムの流れは、以下のようになります。ALFの初期化と終了の間にタスクに関する一連の処理がはいります。ここでタスクとはSPUで実行する処理のことをいいます。
図4. プログラムの流れ
コード中のコメントにある説明も合わせてご確認ください。
SPU側で実行するタスクのコードは以下のようになります。
リスト6. spu/main_spu.c
001: #include <stdio.h>
002: #include <alf_accel.h>
003:
004: int task_main(
005: void* p_task_ctx __attribute__((unused)), // タスクコンテキスト -- ホストからの初期データの受け渡し
006: int instance_id, // インスタンス ID -- プログラムが実行されるSPUの番号
007: int number_of_instance) // インスタンス総数 -- 同時に実行しているSPUの数
008: {
009: printf("[%u/%u] Hello World !!\n",
010: instance_id,
011: number_of_instance);
0012: return 0;
013: }
014:
015: ALF_ACCEL_EXPORT_API_LIST_BEGIN; // 以下3行でタスクメイン関数の名前をホストから参照するためにエクスポートの設定を行う
016: ALF_ACCEL_EXPORT_API("", task_main);
017: ALF_ACCEL_EXPORT_API_LIST_END;
|
この(3)のプログラムでは、使用するSPUの数を1個に設定していますので、このプログラムを実行すると以下のように表示されます。"0/1"の 0はSPUのID番号、1は同時に起動しているSPUの個数です。
図5. プログラムの実行結果
PPU、SPUのプログラムをビルドするための Makefile をそれぞれ以下に示します。
リスト7. Makefile -PPU用
001: DIRS = spu 002: PROGRAM_ppu = hello_ppu+spu 003: IMPORTS = -lalf 004: 005: include $(CELL_TOP)/buildutils/make.footer |
001行目では、SPUのプログラムが存在するディレクトリ名を、002行目では実行ファイル名を指定します。
003行目でALF のライブラリをリンクします。
リスト8. spu/Makefile -SPU用
001: PROGRAM_spu = hello_spu 002: SHARED_LIBRARY_embed = libhello_spu.so 003: 004: IMPORTS = -lalflts 005: 006: INSTALL_DIR = .. 007: INSTALL_FILES = $(SHARED_LIBRARY_embed) 008: 009: include $(CELL_TOP)/buildutils/make.footer |
001行目ではSPUプログラムをコンパイルしたときに生成されるバイナリファイル名を指定しています。
002行目ではSPUプログラムを共有ライブラリに含めるためSHARED_LIBRARY_embed でライブラリ名を指定しています。この002行目で指定するライブラリ名とリスト5のPPU側のコードの019行目で指定するライブラリ名とが一致する必要があります。
004行目でALF のライブラリをリンクします。
006,007行ではこのディレクトリ(/spu)で生成された共有ライブラリをPPU側のディレクトリにコピーしています。
(4)は SPUを8個使って動かすプログラムです。(3)とは使用するSPUの数が1個から8個に変わるだけです。
(3)の main.c の 011行でSPUの数を変えてみましょう。
このときのプログラムの説明や実行結果はLet's LTS: 第 1 回 ALF 軽量タスクサポートの概要をご覧ください。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| サンプルプログラムご使用条件 (必ずお読みください) | readme.pdf | 105KB | HTTP |
| Hello World - ALF LTS版 サンプルコード | Hello_World_AlfLts.zip | 107KB | HTTP |
学ぶために
- Cell/B.E.のアークテクチャーおよびプログラミングの特徴については、Cell Broadband Engine プロセッサの能力を最大限発揮する: アプリケーションのパフォーマンスを引き出すための 25 個のヒント集が参考になります。
- ALF については、Fun with ALF シリーズをご覧ください。
- ALF LTSについてはLet's LTS: 第 1 回 ALF 軽量タスクサポートの概要をご覧ください。
- SDK3.1についてはSDK 解説: SDK バージョン 3.1 の紹介に使用環境や機能についての説明があります。
- 擬似ビルド環境 および make.footerファイルについてはクローズアップ SDK 3.1: 第 2 回 make.footer を使おうで詳細な内容が確認できます。
- XLC SSCについてはXLC で行こう!: 第 1 回 Cell/B.E.用 XLC は2種類あるよで使い方などを確認できます。
- CellSsについてはCell Superscalar はどうだい?: 第 1 回 まずは使ってみようをご覧ください。
- The IBM Semiconductor Solutions Technical Library Power Architecture offerings セクションには、ダウンロードマニュアルや、仕様書などたくさんの有益な情報があります。
- その他の技術情報をtechnology bookstoreで探すことも可能です。
製品や技術を入手するために
- 全ての Cell/B.E. 情報 --関連記事、ディスカッションフォーラム、CellSDK、その他のダウンロード-- は IBM developerWorks の Cell Broadband Engine resource center にあります。
- Cell/B.E. を手に入れるにはこちら: IBM ディープ・コンピューティング | Cell/B.E ブレード
- Cell/B.E.のカスタム製品のお問い合わせはこちら: Contact us about Cell Broadband Engine technology
議論するために
- ディスカッション・フォーラムに参加してください。
- 質問はIBM developerWorks Power Architecture Cell Broadband Engine discussion forum へ投稿してください。
