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

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命令用組み込み関数に対応したものです。

表 3.2 SIMD命令組み込み関数対応表

種類VMXSPU 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」を参照してください。

(http://cell.scei.co.jp/)

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



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