GPGPUで整数計算(8)

素数分布(2)

前にも書いたように、GPUは分岐に弱い。
ある程度まとまったスレッド(16 or 32)が同じ命令を実行するため、
分岐すると、違う命令を実行するスレッドは、
今実行している命令が終わるのを待っていなければならない。
だから、分岐しないようにすれば速くなる。


前回のシェーダプログラムは、
素数かどうかを調べる自然数nを回し、
擬似素数(というか奇数)pを回していて、
2重のループにしていた。
これを一つのループにする。


1回割り算をしたら、次のnとpを計算する。
その際、状況によってnとpは変わってくるが、
これを分岐が起きないように書く。


例えば、
pが5ならnは3増し、その他ならnは2増し、
なら次のように実装する。


n += int(p == 5) + 2;

p == 5をintにキャストすると、trueなら1、falseなら0になる。
万事この調子でコードを書いて、分岐が起きないようにする。



#extension GL_ARB_texture_rectangle : enable
#extension GL_EXT_gpu_shader4 : enable

uniform sampler2DRect texUnit0; // data

uniform int nRep;

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

if(iStart != 0) {
int n = iStart;
int p = 3;
do {
int mod = n % p;
p += 2;
bvec2 b = bvec2(mod == 0, p * p > n);
nPrimes += int(!b.x && b.y);
n += int(any(b)) * 2; // n+2 or n
p += -int(any(b)) * (p - 3); // 3 or p
} while(n < iStart + nRep);
}

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

GPU(今回) : 0.55sec
GPU(前々回) : 2.2sec
CPU : 12.0sec

CPUより約22倍速く、理想に近い速度となった。


以下、細かい点:
bvec2はboolを2つ並べたもの。
anyはどれかがtrueならtrueを返す。
全てtrueならtrueはall。