OpenCLで乱数を取得しようとしています。本当のランダムである必要はありません。シンプルで迅速な何か。
OpenCLには、数千行に相当する実際のランダム並列化ファンシーパンツランダムアルゴリズムがたくさんあります。そのようなものは必要ありません。単純な「random()」は、パターンを簡単に確認できる場合でも問題ありません。
ノイズ機能はありますか?それを使用して乱数を取得する簡単な方法はありますか?
私はこの「ランダムではない」問題を過去数日間解決しており、3つの異なるアプローチを考え出しました。
Xorshift -これに基づいてジェネレータを作成しました。カーネル全体に1つの_uint2
_番号(シード)を指定するだけで、すべての作業項目が独自のRand番号を計算します
_// 'randoms' is uint2 passed to kernel
uint seed = randoms.x + globalID;
uint t = seed ^ (seed << 11);
uint result = randoms.y ^ (randoms.y >> 19) ^ (t ^ (t >> 8));
_
Javaランダム -.next(int bits)
メソッドのコードを使用して乱数を生成しました。今回は、シードとして1つのulong
番号を指定する必要があります。
_// 'randoms' is ulong passed to kernel
ulong seed = randoms + globalID;
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
uint result = seed >> 16;
_
CPUですべてを生成し、1つの大きなバッファーでカーネルに渡すだけです。
グラフの最小支配セットを計算する進化アルゴリズムで3つのアプローチ(ジェネレーター)をすべてテストしました。
私は最初のものから生成された数が好きですが、私の進化アルゴリズムはそうではないようです。
2番目のジェネレーターはいくつかの目に見えるパターンを持つ数値を生成しますが、私の進化アルゴリズムはとにかくそれが好きで、最初のジェネレーターよりも全体が少し速く実行されます。
しかし、3番目のアプローチは、ホスト(cpu)からすべての数値を提供するだけで十分であることを示しています。まず、(私の場合)1536のint32数を生成し、すべてのカーネル呼び出しでそれらをGPUに渡すのは(計算してGPUに転送するには)コストがかかりすぎます。しかし、結局のところ、それは私の以前の試みと同じくらい高速です。また、CPU負荷は5%未満のままです。
ところで、私は MWC64X Random も試しましたが、新しいGPUドライバーをインストールした後、_mul_hi
_関数がビルドに失敗します(AMD Kernel Analyer全体がクラッシュしました)。
以下は、 doc に従って_Java.util.Random
_クラスで使用されるアルゴリズムです。
_(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
_
さまざまな実装については、ドキュメントを参照してください。シードのためにワーカーのIDを渡し、数回ループすると、まともなランダムさが生成されます
または、別の方法として、かなりランダムにオーバーフローするランダムな操作を発生させることもできます。
_ long Rand= yid*xid*as_float(xid-yid*xid);
Rand*=Rand<<32^Rand<<16|Rand;
Rand*=Rand+as_double(Rand);
_
xid=get_global_id(0);
およびyid= get_global_id(1);
現在、リアルタイムパストレーサーを実装しています。パストレースには多数の乱数が必要であることをすでにご存じかもしれません。
GPUで乱数を生成する前に、CPUで乱数を生成し(Rand()を使用)、それらをGPUに渡しました。
それはすぐにボトルネックになりました。
現在、Park-Miller Pseudorandom Number Generator(PRNG)を使用してGPUで乱数を生成しています。
実装は非常に簡単で、非常に優れた結果が得られます。
私は数千のサンプル(0.0から1.0の範囲)を取り、それらを一緒に平均しました。
結果の値は0.5に非常に近かった(これは予想される値です)。異なる実行間で、0.5からの発散は約0.002でした。したがって、非常に均一な分布になります。
これはアルゴリズムを説明する論文です:
http://www.cems.uwe.ac.uk/~irjohnso/coursenotes/ufeen8-15-m/p1192-parkmiller.pdf
そして、CUDA用に最適化された上記のアルゴリズム(OpenCLに簡単に移植できる)に関する論文を以下に示します。 http://www0.cs.ucl.ac.uk/staff/ucacbbl/ftp/ papers/langdon_2009_CIGPU.pdf
これが私がそれをどのように使用しているかの例です:
int Rand(int* seed) // 1 <= *seed < m
{
int const a = 16807; //ie 7**5
int const m = 2147483647; //ie 2**31-1
*seed = (long(*seed * a))%m;
return(*seed);
}
kernel random_number_kernel(global int* seed_memory)
{
int global_id = get_global_id(1) * get_global_size(0) + get_global_id(0); // Get the global id in 1D.
// Since the Park-Miller PRNG generates a SEQUENCE of random numbers
// we have to keep track of the previous random number, because the next
// random number will be generated using the previous one.
int seed = seed_memory[global_id];
int random_number = Rand(&seed); // Generate the next random number in the sequence.
seed_memory[global_id] = *seed; // Save the seed for the next time this kernel gets enqueued.
}
コードは例としてのみ機能します。私はそれをテストしていません。
配列「seed_memory」は、カーネルの最初の実行前に一度だけRand()で埋められます。その後、すべての乱数生成はGPUで行われます。 Rand()で配列を初期化する代わりに、カーネルIDを使用することも可能だと思います。
OpenCLはそのような機能を提供していないようです。ただし、 一部の人々はいくつかの研究を行った で、GPUで適切な乱数を生成するためのBSDライセンスコードを提供しています。
これは、三角関数を使用したOpenCL float疑似ランダムノイズの私のバージョンです。
//noise values in range if 0.0 to 1.0
static float noise3D(float x, float y, float z) {
float ptr = 0.0f;
return fract(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f, &ptr);
}
__kernel void fillRandom(float seed, __global float* buffer, int length) {
int gi = get_global_id(0);
float fgi = float(gi)/length;
buffer[gi] = noise3D(fgi, 0.0f, seed);
}
最初のパラメーターとしてnoise3Dnormalizedインデックス座標を渡し、(たとえばCPUで生成された)ランダムシードを最後のパラメータ。
このカーネルとさまざまなシードで生成されたノイズ画像は次のとおりです。
私も同じ問題を抱えていました。 www.thesalmons.org/john/random123/papers/random123sc11.pdf
ドキュメントはここにあります。 http://www.thesalmons.org/john/random123/releases/latest/docs/index.html
ここからライブラリをダウンロードできます: http://www.deshawresearch.com/resources_random123.html
GPUにはランダム性の優れたソースはありませんが、これは、ホストからランダムシードをカーネルにシードすることで簡単に克服できます。その後は、大量の同時スレッドで動作するアルゴリズムが必要です。
このリンクは、OpenCLを使用したMersenne Twisterの実装について説明しています: Parallel Mersenne Twister 。 NVIDIA SDKにも実装があります。
何故なの?あなたはただ乱数を生成するカーネルを書くことができ、それはより多くのカーネル呼び出しを必要とし、最終的にそれらを必要とする他のカーネルに引数として乱数を渡すことができます