CUDA – cuRAND での乱数生成方法

CUDA では、cuRAND を利用して乱数を生成することができます。
サンプルコードをいくつか掲載します。

目次

シンプルな乱数生成コード

乱数を生成するための、ミニマムのコードです。
GPU で乱数を10個生成し、これをホスト側に転送して標準出力します。

#include <iterator>

#include <curand.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

int main()
{
    size_t n = 10;
    thrust::device_vector<float> dev(n);

    curandGenerator_t gen;
    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);
    curandGenerateUniform(gen, thrust::raw_pointer_cast(dev.data()), n);
    curandDestroyGenerator(gen);

    thrust::host_vector<float> host{dev};
    std::copy(dev.begin(), dev.end(), std::ostream_iterator<float>(std::cout, "\n"));

    return 0;
}

コンパイルするときは -lcurand のオプションでライブラリをリンクする必要があります。

$ nvcc main.cu -lcurand

これを実行すると、次のような乱数列が出力されます。

0.740219
0.920994
0.0390205
0.968963
0.925141
0.44635
0.667319
0.109931
0.470219
0.513194

この実装で生成される乱数には次のような特徴があります。

  • float 型
  • (0.0 ~ 1.0] の範囲
  • 実行のたびに同じ乱数が生成される

NVIDIA のドキュメントによると、生成範囲に 0.0 は含まれず、1.0 は含まれるようです。

The curandGenerateUniform() function is used to generate uniformly distributed floating point values between 0.0 and 1.0, where 0.0 is excluded and 1.0 is included.

https://docs.nvidia.com/cuda/curand/host-api-overview.html#return-values

乱数シードを指定する

シードを指定することで、実行のたびに異なる乱数を生成させることができます。

#include <iterator>
#include <random>

#include <curand.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

int main()
{
    size_t n = 10;
    thrust::device_vector<float> dev(n);

    curandGenerator_t gen;
    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);

    std::random_device seed_gen;
    curandSetPseudoRandomGeneratorSeed(gen, seed_gen());

    curandGenerateUniform(gen, thrust::raw_pointer_cast(dev.data()), n);
    curandDestroyGenerator(gen);

    thrust::host_vector<float> host{dev};
    std::copy(dev.begin(), dev.end(), std::ostream_iterator<float>(std::cout, "\n"));

    return 0;
}

シードそのものの生成には、C++ 標準の std::random_device を利用しています。
これでサンプルした乱数を curandSetPseudoRandomGeneratorSeed に指定します。

double 型の乱数を生成する

double 型の乱数を生成するためには、curandGenerateUniformの代わりにcurandGenerateUniformDoubleを呼び出します。
これに伴い、vector の型なども double に変更します。

#include <iterator>

#include <curand.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

int main()
{
    size_t n = 10;
    thrust::device_vector<double> dev(n);

    curandGenerator_t gen;
    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);

    curandGenerateUniformDouble(gen, thrust::raw_pointer_cast(dev.data()), n);
    curandDestroyGenerator(gen);

    thrust::host_vector<double> host{dev};
    std::copy(dev.begin(), dev.end(), std::ostream_iterator<double>(std::cout, "\n"));

    return 0;
}

そのほか、curandGenerateXXX関数を別のものに置き換えることで、多様な乱数を生成することができます。
詳細は以下のリンクにありますが、整数乱数、正規分布乱数、対数正規分布乱数などを生成することができます。

準乱数を生成する(疑似乱数ではなく)

cuRAND では、準乱数を生成することもできます。
準乱数とは、分布が一様になるように設計された乱数です。

curandCreateGenerator の第二引数に CURAND_RNG_QUASI_DEFAULTを指定します。

#include <iterator>

#include <curand.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

int main()
{

    size_t n = 10;
    thrust::device_vector<float> dev(n);

    curandGenerator_t gen;
    curandCreateGenerator(&gen, CURAND_RNG_QUASI_DEFAULT);
    curandGenerateUniform(gen, thrust::raw_pointer_cast(dev.data()), n);
    curandDestroyGenerator(gen);

    thrust::host_vector<float> host{dev};
    std::copy(dev.begin(), dev.end(), std::ostream_iterator<float>(std::cout, "\n"));

    return 0;
}
1.16415e-10
0.5
0.75
0.25
0.375
0.875
0.625
0.125
0.1875
0.6875

そのほかの指定可能な乱数タイプは、以下のリンクから確認可能です。

範囲が -1.0 ~ 1.0 の乱数を生成する

組み込みの機能はなさそうですので、乱数を生成した後に自前で変換します。
thrust::transformを利用して、2.0 * (乱数 - 0.5) の変換を行います。

#include <iterator>

#include <curand.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

int main()
{

    size_t n = 10;
    thrust::device_vector<float> dev(n);

    curandGenerator_t gen;
    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);
    curandGenerateUniform(gen, thrust::raw_pointer_cast(dev.data()), n);
    curandDestroyGenerator(gen);

    thrust::transform(
        dev.begin(), dev.end(), dev.begin(),
        2.0 * (thrust::placeholders::_1 - 0.5));

    thrust::host_vector<float> host{dev};
    std::copy(dev.begin(), dev.end(), std::ostream_iterator<float>(std::cout, "\n"));

    return 0;
}
0.480439
0.841988
-0.921959
0.937926
0.850281
-0.1073
0.334638
-0.780139
-0.0595628
0.0263873
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次