ほぼ初めてみたいなSIMDプログラミング,略してはじしむ?
とりあえずCellのSPE向けにSIMDってみた.
サンプルとしてメディアンフィルタ……みたいなものを意識して,ある点の周囲の点の座標を合算するものを作ってみた.
まずはCで書いた元プログラム.
C:
for(m=0; m<LOOP; m++){
margin = m*128*128;
for(i=1; i<128-1; i++){
for(k=1; k<128-1; k++){
data2[margin + i*128+k] =
(data1[margin + (i-1)*128+(k-1)]
+ data1[margin + (i-1)*128+(k+1)]
+ data1[margin + (i+1)*128+(k-1)]
+ data1[margin + (i+1)*128+(k+1)]);
}
}
}
マージンが取ってあるのは,繰り返し計算して実行時間を引き延ばすため.めんどいから端点の処理はしない.ただ足しているだけなのは処理を最低限にしないと正しく実行できているかのチェックがツライため.チキンコード.
ちなみにデータ型はunsigned int.
続いてSPE向けのコード.
C:
for(i=1; i<128-1; i++){
for(k=1; k<128-1; k++){
outdata[i*128+k] =
(
indata[(i-1)*128+(k-1)] +
indata[(i-1)*128+(k+1)] +
indata[(i+1)*128+(k-1)] +
indata[(i+1)*128+(k+1)]
);
}
}
データを128*128ごとにDMAってるのでこんなんになる.
続いてSIMD化コード.
C:
for(i=1; i<128-1; i++){
int x, y;
vector unsigned int *v[6];
vector unsigned int *t;
vector unsigned int w[6];
vector unsigned char s0 = (vector unsigned char){
0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b
};
vector unsigned char s1 = (vector unsigned char){
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13
};
for(x=2; x<30; x++){
v[0] = (vector unsigned int *)&indata[(i-1)*128+(x-1)*4];
v[1] = (vector unsigned int *)&indata[(i-1)*128+(x )*4];
v[2] = (vector unsigned int *)&indata[(i-1)*128+(x+1)*4];
v[3] = (vector unsigned int *)&indata[(i+1)*128+(x-1)*4];
v[4] = (vector unsigned int *)&indata[(i+1)*128+(x )*4];
v[5] = (vector unsigned int *)&indata[(i+1)*128+(x+1)*4];
t = (vector unsigned int *)&outdata[i*128+x*4];
w[0] = spu_shuffle(*v[0], *v[1], s0);
w[1] = spu_shuffle(*v[1], *v[2], s1);
w[2] = spu_shuffle(*v[3], *v[4], s0);
w[3] = spu_shuffle(*v[4], *v[5], s1);
w[4] = spu_add(w[0], w[1]);
w[5] = spu_add(w[2], w[3]);
*t = spu_add(w[4], w[5]);
}
for(k=1; k<8; k++){
outdata[i*128+k] =
(
indata[(i-1)*128+(k-1)] +
indata[(i-1)*128+(k+1)] +
indata[(i+1)*128+(k-1)] +
indata[(i+1)*128+(k+1)]
);
}
for(k=120; k<128-1; k++){
outdata[i*128+k] =
(
indata[(i-1)*128+(k-1)] +
indata[(i-1)*128+(k+1)] +
indata[(i+1)*128+(k-1)] +
indata[(i+1)*128+(k+1)]
);
}
}
やりやすいところだけSIMD化した.kループの真ん中あたりだけ.もっとはじっこまで行ける気がするのはとりあえず置いておく.
計算対象の配列のアドレスをvector型のポインタで参照しておいて,vector型で計算すると,計算対象にちゃんと反映されるよ!みたいな感じなのかね.まぁポインタだから当然だけど.なんとなくわかった気がする.
C:
正:t = (vector unsigned int *)&outdata[i*128+x*4];
誤:t = (vector unsigned int *)outdata[i*128+x*4];
みたいなミスを繰り返しまくったんだけど,vector型へのキャストが強力すぎるのかなんなのか,コンパイラが何も言わないのでしばらく頭を抱えた.これはツライ.
で,とりあえずSPE1つで実行時間を測定してみたんだけど……5回繰り返した最短時間を見ると
- SIMD化する前: 13.36145091 sec
- SIMD化した後: 11.95487118 sec
……うーむ,まぁ速くはなったっぽい?
というか,実行時間が結構ばらつきます.実行環境はPS3なんだけど,ハードウェアの特性かな?最長時間を見ると
- SIMD化する前: 16.74284220 sec
- SIMD化した後: 15.84491611 sec
……最短との差は3割.まぁそんなものかしら?
SIMDの効果ってこんなものかなあと疑問を持ったので同じものをSSEあたりでやってみようと思ったけど,何を参考にどうくめばいいのかわからず.1時間以上webを漁ったけどよくわからない.また明日以降に試す.
というか,おとなしくPPE向けのSIMD化でもやろう,うん.
tgbt Cell, プログラミング一般









最近のコメント