3

私はこのlib Random123と関連する引用を見てきました:

1 人の謎の男が私のブースに来て、OpenCL で乱数を生成することについて私が知っていることを尋ねました。Mersenne Twister の実装について彼に話しましたが、彼は感銘を受けませんでした。彼は、整数カウンターとブロック暗号を組み合わせて GPU で乱数を生成する方法を説明する新しいテクニカル ペーパーについて教えてくれました。敬虔な口調で、カウンターベースの乱数ジェネレーター (CBRNG) は、MT よりも統計的ランダム性が高く、はるかに高速な数値を生成すると述べました。

このカーネルを使用してデモを実行できました。

__kernel void counthits(unsigned n, __global uint2 *hitsp) {
    unsigned tid = get_global_id(0);
    unsigned hits = 0, tries = 0;
    threefry4x32_key_t k = {{tid, 0xdecafbad, 0xfacebead, 0x12345678}};
    threefry4x32_ctr_t c = {{0, 0xf00dcafe, 0xdeadbeef, 0xbeeff00d}};
    while (tries < n) {
        union {
            threefry4x32_ctr_t c;
            int4 i;
        } u;
        c.v[0]++;
        u.c = threefry4x32(c, k);
        long x1 = u.i.x, y1 = u.i.y;
        long x2 = u.i.z, y2 = u.i.w;
        if ((x1*x1 + y1*y1) < (1L<<62)) {
            hits++;
        }
        tries++;
        if ((x2*x2 + y2*y2) < (1L<<62)) {
            hits++;
        }
        tries++;
    }
    hitsp[tid].x = hits;
    hitsp[tid].y = tries;
}

私の質問は今、これは実行するたびに同じ乱数を生成しませんか?乱数はグローバルIDに基づいていますか? 毎回新しい乱数を生成するにはどうすればよいですか。カーネルのパラメーターとしてシードを提供し、それを何らかの方法で使用することは可能ですか?

このライブラリを使用していて、その使用についてさらに洞察を得ることができる人はいますか?

4

3 に答える 3

5

はい。サンプル コードは、呼び出されるたびに同じ乱数シーケンスを生成します。

乱数の異なるストリームを取得するには、値 k[1..3] および/または c[1..3] のいずれかを異なる方法で初期化するだけです。コマンドライン引数、環境変数、時刻、保存された状態、/dev/urandom、またはその他のソースからそれらを初期化できます。次の点に注意してください。

a) 2 つの異なる実行でそれらすべてをまったく同じ方法で初期化すると、それらの 2 つの実行は乱数の同じストリームを取得します。

b) 2 つの異なる実行で異なる方法で初期化すると、それらの 2 つの実行は乱数の異なるストリームを取得します。

場合によっては、プロパティ a) が必要になります。プロパティ b) が必要な場合があります。どれが欲しいかを考え、意図したことをしていることを確認してください。

より一般的には、ライブラリ内の関数 (threefry4x32 など) には状態がありません。入力のいずれかのビット (つまり、c または k のいずれかの要素のいずれかのビット) を変更すると、完全に異なるランダムで、統計的に独立した、均一に分散された出力が得られます。

PS 私はライブラリと論文「Parallel Numbers: As Easy as 1, 2, 3」の著者の 1 人です: http://dl.acm.org/citation.cfm?id=2063405

ACM デジタル ライブラリの購読者でない場合、上記のリンクは有料の壁にぶつかる可能性があります。または、このページのリンクをたどって無料でペーパーを入手することもできます。

http://www.thesalmons.org/john/random123/index.html

于 2012-09-02T16:06:01.730 に答える
2

ライブラリ自体についてはお役に立てませんが、OpenCLで乱数を生成する最も一般的な方法は、カーネルへの呼び出しの間にいくつかの状態を保存することです。

乱数ジェネレーターは通常、状態を使用し、そこから新しい状態と乱数が生成されます。実際には、これはまったく複雑ではありません。状態を保持する追加の配列を渡すだけです。私のコードでは、次のように乱数を実装しています。

uint rand_uint(uint2* rvec) {  //Adapted from http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html
    #define A 4294883355U
    uint x=rvec->x, c=rvec->y; //Unpack the state
    uint res = x ^ c;          //Calculate the result
    uint hi = mul_hi(x,A);     //Step the RNG
    x = x*A + c;
    c = hi + (x<c);
    *rvec = (uint2)(x,c);      //Pack the state back up
    return res;                //Return the next result
    #undef A
}
inline float rand_float(uint2* rvec) {
    return (float)(rand_uint(rvec)) / (float)(0xFFFFFFFF);
}
__kernel void my_kernel(/*more arguments*/ __global uint2* randoms) {
    int index = get_global_id(0);
    uint2 rvec = randoms[index];

    //Call rand_uint or rand_float a number of times with "rvec" as argument.
    //These calls update "rvec" with new state, and return a random number

    randoms[index] = rvec;
}

。。。次に、RNGの状態をランダムに保持する追加の配列を渡すだけです。実際には、この配列を作業項目ごとに異なる方法でシードする必要があります。

于 2012-07-01T21:23:25.837 に答える
1

0xdecafbad0xfacebead0x123456780xf00dcafe0xdeadbeef0xbeeff00d恣意的に選んだ数字であり、特別なものではありません。他の数字 (0 も含む) を代わりに使用できます。サンプル コードにコメントを追加します。

それらのいずれも、渡す変数に置き換えることができます。出力ランダム「ストリーム」で望ましくない繰り返しを回避するための唯一の要件は、(c, k) 入力タプルの繰り返しを避けることです。サンプル コードでは、スレッド ID とループ インデックスを使用して一意性を確保していますが、変数を簡単に追加して一意性を確保できます。たとえば、ホスト コードでカーネル呼び出しをカウントし、そのカウンターを渡し、要素の 1 つの代わりにそれを使用します。 kまたはcの。

ところで、「カウンターベースの乱数ジェネレーター」という名前にもかかわらず、入力 (c, k) が「カウンター」であるという要件はありません。繰り返す。

于 2012-09-02T20:11:45.060 に答える