GPGPUで整数計算(10)

for文

例えば、128*128のデータを用意して、それになんらかの値が入っている。シェーダプログラムのほうは、0ならそのまま抜けて、それ以外なら重い計算(1000万回の繰り返し計算)をするようになっている。


uniform sampler2DRect texUnit0; // data

uniform int nRep;

void main(void) {
int nPrimes = 0;
vec4 color = texRECT(texUnit0, gl_FragCoord.xy);
int x = int(color.x);

if(x != 0) {
for(int n = 0; n < nRep; n++)
x = 1 - x;
}

gl_FragColor = vec4(x, 0, 0, 0);
}

color.xにすべて1が入っているとき、この処理は約5秒かかった。
しかし、すべて0が入っているとき、約2.5秒かかった。繰り返し計算を行わずにすぐに抜けるはずなのに。
こうすると、ほとんど時間がかからない。


uniform sampler2DRect texUnit0; // data

uniform int nRep;

void main(void) {
int nPrimes = 0;
vec4 color = texRECT(texUnit0, gl_FragCoord.xy);
int x = int(color.x);

if(x != 0) {
int n = 0;
while(n < nRep) {
x = 1 - x;
n++;
}
}

gl_FragColor = vec4(x, 0, 0, 0);
}

すなわち、forでなくwhileを使うと速くなる。
また、同じforでも次のようだとほとんど時間がかからない。


uniform sampler2DRect texUnit0; // data

void main(void) {
int nPrimes = 0;
vec4 color = texRECT(texUnit0, gl_FragCoord.xy);
int x = int(color.x);
int nRep = int(color.y);

for(int n = 0; n < nRep; n++)
x = 1 - x;

gl_FragColor = vec4(x, 0, 0, 0);
}

ここで、繰り返し回数nRepはuniformではなく、テクスチャで入力した値となっている。


たぶん、forの繰り返し回数がuniformやconstのときは、入力が0のときもそうでないときも同じような処理をして結果的に速くなるように、コンパイラが変な最適化をしている。
ここで、なぜ全部0のときに速くないと困るかというと、
例えば、1万個のデータを処理したいとしたとき、テクスチャの大きさは2のべきだから、16384個のデータを用意しなければならない。だから、後ろの6384個にはある数値(例えば0)を入れておいて、0だったらすぐに抜けるようにする。その0がバラバラに配置されていれば、同じ処理をさせる原則から外れて結果的に実行速度が遅くなるかもしれないが、ある程度固まっていればそのようなことはなくなる。このあたりは次回の記事で詳しく追究したい。


ともかく、実行速度を遅くしている要因になっているかもしれないので、for文には注意。