2.3 簡単なSIMD演算
出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう
ここでは、加算の例題を用いてSIMD演算による簡単な四則演算について解説します。スカラ演算とSIMD演算を比較しながら、演算の効率とデータの扱い方を中心に解説します。
2.3.1 加算処理をおこなうプログラム
ここでは4個の加算処理の結果を求めるプログラムを例に考えます。まず、リスト (2-1) に従来のプログラミングであるスカラ演算による加算処理プログラムを示します。
リスト (2-1) スカラ演算による加算処理
1 int a[4] = { 1, 3, 5, 7 };
2 int b[4] = { 2, 4, 6, 8 };
3 int c[4];
4
5 c[0] = a[0] + b[0]; // 1 + 2
6 c[1] = a[1] + b[1]; // 3 + 4
7 c[2] = a[2] + b[2]; // 5 + 6
8 c[3] = a[3] + b[3]; // 7 + 8
スカラ演算では「単一データ = 単一データ + 単一データ」の構成で演算をおこないます。そのため、4個の加算結果を求めるためには、リスト (2-1) の5行目~8行目に示すように4回の加算命令を逐次的に実行する必要があります。
次に、SIMD演算における加算処理プログラムを示します。
リスト (2-2) SIMD演算による加算処理
1 int a[4] __attribute__((aligned(16))) = { 1, 3, 5, 7 };
2 int b[4] __attribute__((aligned(16))) = { 2, 4, 6, 8 };
3 int c[4] __attribute__((aligned(16)));
4
5 vector signed int *va = (vector signed int *) a;
6 vector signed int *vb = (vector signed int *) b;
7 vector signed int *vc = (vector signed int *) c;
8
9 *vc = vec_add(*va, *vb); // 1 + 2, 3 + 4, 5 + 6, 7 + 8
VMXでは、各SIMD命令に対応した組み込み関数が用意されています。リスト (2-2) の9行目で利用されているvec_add()関数は、VMXの加算命令に相当する組み込み関数です。
SIMD演算では、「複数データ = 複数データ + 複数データ」の処理をおこないます。SIMD演算は1命令で演算できるデータ数がスカラ演算に比べて多く、演算結果を求めるための命令数を抑えることができるため、処理時間の短縮効果があります。
第2.2節で述べたとおり、変数a、b、cはベクタデータとして参照されるため、キーワード__attribute__を用いて16バイト境界に揃えられるようにaligned属性が指定されています。
2.3.2 例題プログラムの解説
それでは、例題プログラムのソースコード全体について解説します。
例題プログラム (2-1) 加算処理プログラム全体
1 #include <stdio.h>
2 #include <altivec.h>
3
4 int a[4] __attribute__((aligned(16))) = { 1, 3, 5, 7 };
5 int b[4] __attribute__((aligned(16))) = { 2, 4, 6, 8 };
6 int c[4] __attribute__((aligned(16)));
7
8 int main(int argc, char **argv)
9 {
10 vector signed int *va = (vector signed int *) a;
11 vector signed int *vb = (vector signed int *) b;
12 vector signed int *vc = (vector signed int *) c;
13
14 *vc = vec_add(*va, *vb); // 1 + 2, 3 + 4, 5 + 6, 7 + 8
15
16 printf("c[0]=%d, c[1]=%d, c[2]=%d, c[3]=%d\n", c[0], c[1], c[2], c[3]);
17
18 return 0;
19 }
⇒例題プログラム (2-1) のソースコードはこちらから
| 2行目 | VMXを使用する時に必要なヘッダファイル "altivec.h" をインクルードします。なお、VMX命令はAltiVecテクノロジを利用しているため、ヘッダファイルなどにAltiVecという文字列が含まれています。 |
| 4行目~6行目 | 入力される配列データa、bと合計値を格納するためのスカラ配列cを定義します。aligned属性で16バイト境界に変数のアドレスを揃えています。 |
| 10行目~12行目 | SIMD演算に必要な変数を定義します。変数va、vb、vcは、SIMD演算で用いるベクタ変数へのポインタで、スカラ配列をベクタデータとして参照するために使用されます。 |
| 14行目 | 加算命令を実行するvec_add()関数を実行します。vec_add()関数は、vaとvbの対応した各要素を加算した結果を、vcの対応した要素に格納します。 |
| 16行目 | 計算結果を標準出力に表示します。 |
2.3.3 プログラムのコンパイルと実行
プログラムのコンパイルは、gccコマンドを利用します。VMXを利用する場合は、コンパイルオプションとして、-maltivecオプションと-mabi=altivecオプションを指定します。
用例 (2-5) プログラムのコンパイル
$ gcc –maltivec –mabi=altivec vec_add.c –o vec_add.elf
プログラムの実行は、従来のプログラムの実行方法と同じで、シェルプロンプト上でELF実行ファイルを指定して実行します。
用例 (2-6) プログラムの実行
$ ./vec_add.elf c[0]=3, c[1]=7, c[2]=11, c[3]=15 $
2.3.4 VMX命令組込み関数 (Intrinsics)
本節では、VMX加算命令に対応したvec_add()関数を紹介しました。加算命令以外の組込み関数についても、算術演算命令、ビット演算命令、比較演算命令など数多くのVMX命令に対応した組込み関数が提供されています。表 2.4に組込み関数の一例を示します。
| 種類 | 関数名 | 機能 |
|---|---|---|
| 算術演算命令 | vec_add(a, b) | ベクタa、bの各要素を加算します。 |
| vec_sub(a, b) | ベクタa、bの各要素を減算します。 | |
| vec_madd(a, b, c) | ベクタa、bの各要素を乗算し、ベクタcの各要素を加算します。 | |
| vec_re(a) | ベクタaの各要素の逆数を計算します。 | |
| vec_rsqrte(a) | ベクタaの各要素の逆数平方根を計算します | |
| 論理演算命令 | vec_and(a, b) | ベクタa、bのビット毎の論理積を求めます。 |
| vec_or(a, b) | ベクタa、bのビット毎の論理和を求めます。 | |
| シフト・ローテート命令 | vec_sr(a, b) | ベクタaの各要素をベクタbの各要素で指定したビット数分だけ右シフトします。 |
| vec_rl(a, b) | ベクタaの各要素をベクタbの各要素で指定したビット数分だけ左ローテートします。 | |
| ビット演算命令 | vec_perm(a, b, c) | ベクタcで指定されたバイトパターンに基づいてベクタa、bの各要素を並べ替えます。 |
| vec_sel(a, b, c) | ベクタcで指定されたビットパターンに基づいてベクタa、bの各ビットを選択します。 | |
| 比較分岐命令 | vec_cmpeq(a, b) | ベクタa、bの各要素が等しいか比較します。 |
| vec_cmpgt(a, b) | ベクタaの各要素がベクタbの各要素より大きいか比較します。 | |
| 変換命令 | vec_ctf(a, b) | 整数ベクタaの各要素を2bで除算し、浮動小数点へ変換します。 |
| vec_ctu(a, b) | 浮動小数点ベクタaの各要素を2bで乗算し、符号なし整数へ変換します。 | |
| 定数生成命令 | vec_splat(a, b) | ベクタaのb番目の要素を展開したベクタデータを生成します。 |
| vec_splat_s32(a) | スカラリテラルaを4個の符号付き32ビット量データに展開したベクタデータを生成します。 |
2.3.5 参考資料
表 2.4に示した関数はVMX命令の代表的なものです。さらに詳しく学習したい方はFreescale Semiconductor社が公開している「AltiVec Technology Programing Interface Manual」の第4章を参照してください。
