2.5 条件分岐の削除
出典: PS3 Linux Information Site / Cell/B.E.のパワーを体験しよう
スカラ演算では、条件によって処理内容を変えたい場合、条件分岐を用います。しかし、SIMD演算でスカラ演算と同様に条件分岐を用いると、ベクタデータの各要素を取り出して逐次的に処理する必要があり、効率的ではありません。そのため、SIMD演算では条件分岐を使わずに他の手法を用いて同等の結果を求めます。
2.5.1 SIMD演算における条件分岐
スカラ演算では、条件ごとに異なる処理をおこないたい場合、図 2.12 (a)のように条件判定をおこなって処理を分岐します。一方、SIMD演算では、図 2.12 (b)のように条件分岐を用いずに処理することで、同じ結果を効率的に得ることができます。
![]() |
ここからは、図2.12 (b)の各手順について順に説明します。まず、図 2.13に示すように条件の真偽を評価します。
![]() |
次に、図 2.14に示すように「条件が真の場合」と「条件が偽の場合」の処理結果を求めます。
![]() |
最後に、図 2.15に示すように判定結果に基づいて、真の場合の処理結果と、偽の場合の処理結果をベクタデータの各要素に1回の操作で選択して格納します。
![]() |
このように、SIMD演算ではあらかじめ条件に沿った演算処理をおこなっておき、最後に必要な結果を選ぶことによりスカラ演算の条件分岐と同等の結果を得ています。
2.5.2 比較と選択
それでは、実際にベクタデータに対する条件分岐の削除方法について解説します。SIMD演算では、比較演算命令とビット選択命令を用いて、条件分岐を削除します。具体的には、vec_cmpgt()関数などの比較演算命令に対応した組み込み関数を用いて、条件の事前評価をおこない、ビット選択命令に対応したvec_sel()関数を用いて処理結果の選択をおこないます。
vec_cmpgt()関数では、第1引数vaと第2引数vbの各要素を比較して、vaの要素がvbの要素より大きければ、対応する要素のすべてのビットに1をセットし、そうでなければ対応する要素のすべてのビットに0をセットし、新しいベクタデータを生成して返します。
用例 (2-8) vec_cmpgt()関数の記述方法
vpat = vec_cmpgt(va, vb);
図 2.16 にvec_cmpgt()関数の処理イメージを示します。
図 2.17にvec_cmpgt()関数とvec_sel()関数の処理イメージを示します。
2.5.3 差分を計算するプログラム
それでは条件分岐の例題として、2つの要素の差分の絶対値を求めるプログラムを考えてみます。
リスト (2-4) スカラ演算による差分計算
1 int a[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
2 int b[16] = { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
3 int c[16];
4
5 int i;
6
7 for (i = 0; i < 16; i++) {
8 if (a[i] > b[i]) {
9 c[i] = a[i] - b[i];
10 } else {
11 c[i] = b[i] - a[i];
12 }
13 }
スカラ演算では、forループ内でスカラ配列aとbの各要素の大小を逐次的に比較します。aの要素が大きければa[i]からb[i]を減算し、そうでなればb[i]からa[i]を減算します。差分計算をおこなうたびに、条件分岐処理が必要になります。
次にSIMD演算における差分を計算するプログラムです。ここでは、大小比較にvec_cmpgt()関数を用います。
リスト (2-5) SIMD演算による差分計算
1 int a[16] __attribute__((aligned(16))) = { 1, 2, 3, 4, 5, 6, 7, 8,
2 9, 10, 11, 12, 13, 14, 15, 16 };
3 int b[16] __attribute__((aligned(16))) = { 16, 15, 14, 13, 12, 11, 10, 9,
4 8, 7, 6, 5, 4, 3, 2, 1 };
5 int c[16] __attribute__((aligned(16)));
6
7 vector signed int *va = (vector signed int *) a;
8 vector signed int *vb = (vector signed int *) b;
9 vector signed int *vc = (vector signed int *) c;
10 vector signed int vc_true, vc_false;
11 vector unsigned int vpat;
12
13 int i;
14
15 for (i = 0; i < 4; i++) {
16 vpat = vec_cmpgt(va[i], vb[i]);
17 vc_true = vec_sub(va[i], vb[i]);
18 vc_false = vec_sub(vb[i], va[i]);
19 vc[i] = vec_sel(vc_false, vc_true, vpat);
20 }
SIMD演算では、スカラ演算のような条件分岐はおこなわず、まず4つの要素をまとめて比較し、事前に真の場合の差分計算と偽の場合の差分計算をしておきます。最後に条件に応じて計算結果を選択しベクタ変数vcに格納します。
2.5.4 例題プログラムの解説
それでは、例題プログラムのソースコード全体について解説します。
例題プログラム (2-3) 差分計算プログラム全体
1 #include <stdio.h>
2 #include <altivec.h>
3
4 int a[16] __attribute__((aligned(16))) = { 1, 2, 3, 4, 5, 6, 7, 8,
5 9, 10, 11, 12, 13, 14, 15, 16 };
6 int b[16] __attribute__((aligned(16))) = { 16, 15, 14, 13, 12, 11, 10, 9,
7 8, 7, 6, 5, 4, 3, 2, 1 };
8 int c[16] __attribute__((aligned(16)));
9
10 int main(int argc, char **argv)
11 {
12 vector signed int *va = (vector signed int *) a;
13 vector signed int *vb = (vector signed int *) b;
14 vector signed int *vc = (vector signed int *) c;
15 vector signed int vc_true, vc_false;
16 vector unsigned int vpat;
17
18 int i;
19
20 for (i = 0; i < 4; i++) {
21 vpat = vec_cmpgt(va[i], vb[i]);
22 vc_true = vec_sub(va[i], vb[i]);
23 vc_false = vec_sub(vb[i], va[i]);
24 vc[i] = vec_sel(vc_false, vc_true, vpat);
25 }
26
27 for (i = 0; i < 16; i++) {
28 printf("c[%02d]=%2d\n", i, c[i]);
29 }
30
31 return 0;
32 }
⇒例題プログラム (2-3) のソースコードはこちらから
| 4行目~8行目 | 入力される配列データa、bと差分を格納するためのスカラ配列cを定義します。 |
| 12行目~16行目 | SIMD演算に必要な変数を定義します。変数va、vb、vcは、SIMD演算で用いるベクタ変数へのポインタで、スカラ配列をベクタデータとして参照するために使用されます。 |
| 20行目~25行目 | forループ中では、vaとvbの各要素の大小の比較、条件が真の場合の差分計算と、条件が偽の場合の差分計算をおこない、それぞれの計算結果を変数vc_trueとvc_falseに格納します。最後に、vec_sel()関数を用いて変数vcに差分値を選択して格納します。 |
| 27行目~29行目 | 各要素の差分を標準出力に出力します。 |
| 「第2.4節」へ戻る | 「第2章目次」 | 「演習問題 (2-1)」へ進む |
| 「チュートリアル目次」 |






