本サイトは、Cell トータルソリューションカンパニー -フィックスターズの技術者有志が運営するサイトです。

4.3 PPEとSPEの共有データ表現

出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう

 次に、PPEとSPEとの間でのデータ共有における注意事項について解説します。通常、効率の良いプログラミングをするために、複数のソースコードで共有されるデータ型は、共通のヘッダファイルに定義されます。このようにヘッダファイルに定義されたデータ型をPPEプログラムとSPEプログラムで共有する場合、そのデータ型の定義に注意をする必要があります。いて解説します。

4.3.1 PPEとSPEのデータ表現の違い

 C言語では、例えば、int型は32ビットで表現するといったような決まりはないため、同じデータ型であっても、さまざまな表現があり得ます。Cellでは、以下のような「ILP32」と「LP64」という2種類のデータ表現のいずれかが用いられます。

(1) ILP32

int型、long型、ポインタ型のいずれも、32ビット長で表現されます。

(2) LP64 (I32LP64)

int型は32ビット長、long型、ポインタ型は、64ビット長で表現されます。

 ここで、Iは整数 (Integer)、Lは長整数 (Long Integer)、Pはポインタ (Pointer) をそれぞれ意味します。なお、ILP32、LP64のいずれでもlong long型は64ビット長で表現されます。

 PPEプログラムには、ILP32を用いる32ビットプログラムと、LP64を用いる64ビットプログラムの2種類があります。また、SPEプログラムでは、ILP32を用います。PPEプログラムとSPEプログラムにおける各データ型のサイズをまとめると表 4.3のようになります。

表 4.3 データ表現の違い

   データ表現

データサイズ
PPEプログラムSPEプログラム
(ILP32)
32ビットプログラム
(ILP32)
64ビットプログラム
(LP64)
int 32ビット 32ビット 32ビット
long 32ビット 64ビット 32ビット
long long 64ビット 64ビット 64ビット
ポインタ 32ビット 64ビット 32ビット

 表中で赤字で示したように、64ビットのPPEプログラムでは、long型とポインタ型のデータサイズがSPEプログラムと異なります。また、上の表では、すべてのポインタ型が同列に並べられていますが、PPEプログラムとSPEプログラムではポインタが指し示すアドレスの意味が異なり、PPEプログラムでは実効アドレスを、SPEプログラムではLSアドレスを指していることに注意してください。

4.3.2 PPEとSPEのデータ共有における注意点

 ここでは、第3.3節で紹介した例題プログラムで利用したDMA転送パラメータを例に、PPEとSPEでデータを共有する際の注意点について考えてみます。この例題プログラムでは、リスト (4-4) のように実効アドレスのデータ表現として64ビットデータ型であるlong long型を用いてアドレスを表現していました。

リスト (4-4) 共有されるDMA転送パラメータのデータ表現 (符号なしlong long型)

typedef struct {
    unsigned long long ea_in;      /* Effective address of input data  */
    unsigned long long ea_out;     /* Effective address of output data */
    int size;
    int pad[3];
} abs_params_t;

 ea_inea_outはアドレスなので、リスト (4-5) のようにポインタ型で記述するのが自然に見えます。実際、PPEプログラムとSPEプログラム両方が同じデータ表現の場合、つまりILP32であれば、確かに期待した結果を得ることはできます。

リスト (4-5) 共有されるDMA転送パラメータの誤ったデータ表現 (ポインタ型)

typedef struct {
    int *ea_in;                    /* Effective address of input data  */
    int *ea_out;                   /* Effective address of output data */
    int size;
    int pad;
} abs_params_t;

 しかし、ポインタ型のデータサイズは、64ビットのPPEプログラムでは64ビットであるのに対して、SPEプログラムでは32ビットであるため、リスト (4-5) の構造体のデータ配置も表 4.4のように異なったものとなります。そのため、同じデータをリスト (4-5) の構造体として参照しようとしても、PPEプログラムとSPEプログラムとでは、表 4.4に示すように互いに異なった場所を参照してしまい、正しい結果が得られません。


表 4.4 構造体のデータ配置

PPEプログラムSPEプログラム
32ビットプログラム64ビットプログラム
画像:TABLE-04-04-1.png 画像:TABLE-04-04-2.png 画像:TABLE-04-04-3.png

 このようにDMA転送などによって共有されるデータの中にlong型やポインタ型が含まれる場合には、正常にデータを参照できない場合がありますので注意してください。

 さらに、この例では、実効アドレスのデータサイズが異なるという以前に、SPEプログラムではLSアドレスを指しているべきポインタを用いて実効アドレスを保持していることが誤りです。例えば、PPEプログラムからint *型として実効アドレスを受け取ったからといって、SPEプログラムでDMA転送せずに、そのままポインタを介してデータを扱えるわけではありません。実際に、リスト (4-6) のようなソースコードは、SPEプログラムでは正常に動作しないことが分かると思います。

リスト (4-6) SPEプログラムでの誤った実効アドレス参照

volatile abs_params_t param __attribute__((aligned(16)));

for (i = 0; i < param.size; i++) {
    *(param.ea_out + i) = *(param.ea_in + i) + 10;
}

4.3.3 データ表現の違いに依存しないプログラミング

 このように、PPEプログラムとSPEプログラムとでは、データ表現によってデータ型のサイズが異なる場合があり、共有データにそのような型のデータが含まれると、問題がおこります。そこで、以下のいずれかのテクニックを用いて、データ表現に依存しないプログラミングをおこなうことによって、これらの問題を解決します。

(1) long型とポインタ型は使わない

 long型とポインタ型は、PPEとSPEでサイズが異なる場合があるので使わないようにします。32ビット長の整数データを扱う場合は、明示的にint型を使い、64ビット長の整数データを扱う場合は、明示的にlong long型を使います。また、実効アドレスには、符号なしlong long型を使います。PPEプログラムのポインタ型で表現されている実効アドレスを符号なしlong long型に変換する場合には、リスト (4-7) のようにキャストします。ここで、符号なしlong型に一度キャストしているのは、符号拡張を抑制するためです。

リスト (4-7) 符号なしlong long型を用いて実効アドレスを表現する

int *in, *out;
abs_params.ea_in  = (unsigned long long) (unsigned long) in;
abs_params.ea_out = (unsigned long long) (unsigned long) out;

(2) 明示的なデータ長を持つデータ型を利用する

 もう一つの手法として、stdint.hで定義されたデータ型を利用するという方法があります。PPEプログラムとSPEプログラムとで共有する全てのデータ型について、stdint.hで定義されているint32_t型やint64_t型といった明示的なデータ長を持つデータ型を使います。また、実効アドレスには、リスト (4-8) のようにuint64_t型を使います。

リスト (4-8) 明示的なデータ型の定義

typedef struct {
    uint64_t ea_in;
    uint64_t ea_out;
    uint32_t size;
    uint32_t pad[3];
} abs_params_t

PPEプログラムのポインタ型で表現されている実効アドレスをuint64_t型へキャストする方法は、リスト (4-9) のようになります。ここで、uintptr_t型はポインタ型と同じサイズの整数型です。

リスト (4-9) stdint.hを利用して実効アドレスを表現する

#include <stdint.h>

int *in, *out;
abs_params.ea_in  = (uint64_t) (uintptr_t) in;
abs_params.ea_out = (uint64_t) (uintptr_t) out;

 このように、ポインタ型やlong型といったデータ表現に依存するデータ型の利用を回避することで、32ビット、64ビット両方のPPEプログラムで、SPEプログラムと正しくデータを共有できます。

4.3.4 PPEプログラムのコンパイル方法

 32ビットと64ビットのPPEプログラムは、用例 (4-1)、(4-2) のようにコンパイル時に-m32もしくは-m64オプションをそれぞれ指定することによって作成できます。

用例 (4-1) 32ビットPPEプログラムのコンパイル方法

$ gcc -m32 sample_ppe.c

用例 (4-2) 64ビットPPEプログラムのコンパイル方法

$ gcc -m64 sample_ppe.c

 また、PPEプログラムのコンパイル時に-mオプションを明示的に指定しない場合、32ビットプログラムが作成されるか、64ビットプログラムが作成されるかはコンパイラに依存します。同じgccであっても、開発環境によって生成されるPPEプログラムが異なる可能性もあるため、データサイズを意識したプログラミングをすることは、正しく動作するプログラムを作成する上で非常に重要になります。



第4.2節」へ戻る 第4章目次 第4.4節」へ進む
チュートリアル目次
表示
個人用ツール
Open Source Projects
ツールボックス