3.4 SPEにおけるSIMDプログラミング
出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう
第2章では、VMX命令を用いたPPEにおけるSIMDプログラミングについて解説しました。PPEにはVMX命令が実装されているように、SPEにおいてもSPU SIMD命令と呼ばれるSIMD命令が実装されています。ここでは、SPU SIMD命令を用いたSPEにおけるSIMDプログラミングについて解説します。
3.4.1 SPU SIMD命令を利用した絶対値計算プログラム
SIMD演算の考え方やプログラミング手法は、PPEとSPEとで大きく異なることはありません。基本的には、VMX命令やSPU SIMD命令に対応した組み込み関数を用いることでSIMDプログラミングがおこなえます。
リスト (3-5) は、演習問題 (2-3) の解答プログラムの一部です。これは、VMX命令を用いて絶対値の計算をしています。
リスト (3-5) VMX命令による絶対値計算 (PPE用プログラム) (赤字:VMX命令組み込み関数)
1 for (i = 0; i < SIZE/4; i++) {
2 vpat = vec_cmpgt(vin[i], vzero);
3 vin_negative = vec_madd(vin[i], vminus, vzero);
4 vout[i] = vec_sel(vin_negative, vin[i], vpat);
5 }
このプログラムをSPU SIMD命令を用いて書き換えると、リスト (3-6) のようになります。
リスト (3-6) SPU SIMD命令による絶対値計算 (SPE用プログラム) (赤字:SPU SIMD命令組み込み関数)
1 for (i = 0; i < SIZE/4; i++) {
2 vpat = spu_cmpgt(vin[i], vzero);
3 vin_negative = spu_madd(vin[i], vminus, vzero);
4 vout[i] = spu_sel(vin_negative, vin[i], vpat);
5 }
それぞれのプログラムを比較して分かるように、SPE側ではSPU SIMD命令に対応した組み込み関数を用いることで、PPE側と同じようにSIMDプログラミングがおこなえます。
3.4.2 SPU SIMD命令組み込み関数 (Intrinsics)
VMX命令と同じようにSPU SIMD命令にも対応した組み込み関数があります。SPU SIMD命令組み込み関数の多くは、VMX命令の組み込み関数と1対1で対応しています。表 3.2に、VMX命令組み込み関数とSPU SIMD命令組み込み関数の対応表を示します。ここに掲載された組み込み関数は、表 2.4で紹介した代表的なVMX命令用組み込み関数に対応したものです。
| 種類 | VMX | SPU SIMD | 機能 |
|---|---|---|---|
| 算術演算命令 | vec_add(a,b) | spu_add(a,b) | ベクタa, bの各要素を加算します。 |
| vec_sub(a,b) | spu _sub(a,b) | ベクタa, bの各要素を減算します。 | |
| vec_madd(a,b,c) | spu_madd(a,b,c) | ベクタa, bの各要素を乗算し、ベクタcの各要素を加算します。 | |
| vec_re(a,b) | spu_re(a,b) | ベクタaの各要素の逆数を計算します。 | |
| vec_rsqrte(a) | spu_rsqrte(a) | ベクタaの各要素の逆数平方根を計算します。 | |
| 論理演算命令 | vec_and(a,b) | spu_and(a,b) | ベクタa, bのビット毎の論理積を求めます。 |
| vec_or(a,b) | spu_or(a,b) | ベクタa, bのビット毎の論理和を求めます。 | |
| シフト・ローテート命令 | vec_sr(a,b) | spu_rlmask(a,b) | ベクタaの各要素をベクタbの各要素で負の値で指定したビット数分だけ右シフトします。 |
| vec_rl(a,b) | spu_rl(a,b) | ベクタaの各要素をベクタbの各要素で指定したビット数分だけ左ローテートします。 | |
| ビット演算命令 | vec_perm(a,b,c) | spu_shuffle(a,b,c) | ベクタcで指定されたバイトパターンに基づいてベクタa,bの各要素を並べ替えます。 |
| vec_sel(a,b,c) | spu_sel(a,b,c) | ベクタcで指定されたビットパターンに基づいてベクタa, bの各ビットを選択します。 | |
| 比較命令 | vec_cmpeq(a,b) | spu_cmpeq(a,b) | ベクタa, bの各要素が等しいか比較します。 |
| vec_cmpgt(a,b) | spu_cmpgt(a,b) | ベクタaの各要素がベクタbの各要素より大きいか比較します。 | |
| 変換命令 | vec_ctf(a,b) | spu_convtf(a,b) | 整数ベクタaの各要素を2bで除算し、浮動小数点へ変換します。 |
| vec_ctu(a,b) | spu_convtu(a,b) | 浮動小数点ベクタaの各要素を2bで乗算し、符号なし整数へ変換します。 | |
| 定数生成命令 | vec_splats_s32(a) | spu_splats(a) | スカラリテラルaを4個の符号付き32ビット量データに展開したベクタデータを作成します。 |
表 3.2に紹介した組み込み関数は代表的なものです。SPU SIMD命令組み込み関数についてさらに詳しく学習したい方は、IBM社が公開している「C/C++ Language Extentions for Cell Broadband Engined Architecture」を参照してください。
3.4.3 例題プログラムの解説
それでは、SPU SIMD命令を用いた絶対値計算プログラムのソースコード全体について解説します。
PPEプログラム、SPEプログラムともに第3.3節で紹介した例題プログラムを流用しています。PPEプログラムは、実行されるSPEプログラムのELF実行ファイル名が異なる以外は同じであるため解説を省略します。SPEプログラムについては、SPU SIMD命令に書き換えた箇所のみを解説します。
例題プログラム (3-5) SPU SIMD命令を利用した絶対値計算プログラム (SPE用プログラム)
1 #include <stdio.h>
2 #include <spu_intrinsics.h>
3 #include <spu_mfcio.h>
4
5 #define MAX_BUFSIZE (128)
6
7 float in_spe[MAX_BUFSIZE] __attribute__((aligned(16)));
8 float out_spe[MAX_BUFSIZE] __attribute__((aligned(16)));
9
10 typedef struct {
11 unsigned long long ea_in;
12 unsigned long long ea_out;
13 unsigned int size;
14 int pad[3];
15 } abs_params_t;
16
17 abs_params_t abs_params __attribute__((aligned(16)));
18
19 int main(unsigned long long spe, unsigned long long argp, unsigned long long envp)
20 {
21 int i;
22 int tag = 1;
23
24 vector float *vin = (vector float *) in_spe;
25 vector float *vout = (vector float *) out_spe;
26 vector float vin_negative;
27 vector unsigned int vpat;
28
29 vector float vzero = (vector float) { 0, 0, 0, 0 };
30 vector float vminus = (vector float) { -1, -1, -1, -1 };
31
32 /* DMA Transfer 1 : GET input/output parameters */
33 spu_mfcdma64(&abs_params, mfc_ea2h(argp), mfc_ea2l(argp),
34 sizeof(abs_params_t), tag, MFC_GET_CMD);
35 spu_writech(MFC_WrTagMask, 1 << tag);
36 spu_mfcstat(MFC_TAG_UPDATE_ALL);
37
38 /* DMA Transfer 2 : GET input data */
39 spu_mfcdma64(vin, mfc_ea2h(abs_params.ea_in), mfc_ea2l(abs_params.ea_in),
40 abs_params.size * sizeof(float), tag, MFC_GET_CMD);
41 spu_writech(MFC_WrTagMask, 1 << tag);
42 spu_mfcstat(MFC_TAG_UPDATE_ALL);
43
44 /* Calculate absolute values with vector operation */
45 for (i = 0; i < abs_params.size/4; i++) {
46 vpat = spu_cmpgt(vin[i], vzero);
47 vin_negative = spu_mul(vin[i], vminus);
48 vout[i] = spu_sel(vin_negative, vin[i], vpat);
49 }
50
51 /* DMA Transfer 3 : PUT output data */
52 spu_mfcdma64(vout, mfc_ea2h(abs_params.ea_out), mfc_ea2l(abs_params.ea_out),
53 abs_params.size * sizeof(float), tag, MFC_PUT_CMD);
54 spu_writech(MFC_WrTagMask, 1 << tag);
55 spu_mfcstat(MFC_TAG_UPDATE_ALL);
56
57 return 0;
58 }
| 45行目~49行目 | SPU SIMD演算を用いて絶対値を求めます。 |
SIMD演算を用いた絶対値計算プログラムのソースコードは以下のリンクからダウンロードできます。
ファイルダウンロード:vec_abs.tar.gz
