Generating random numbers with uniform distributio

2020-02-05 05:10发布

I need to generate a vector with random numbers between 0.0 and 1.0 using Thrust. The only documented example I could find produces very large random numbers (thrust::generate(myvector.begin(), myvector.end(), rand). I'm sure the answer is simple, but I would appreciate any suggestions.

标签: c++ cuda thrust
4条回答
兄弟一词,经得起流年.
2楼-- · 2020-02-05 05:15

Thrust has random generators you can use to produce sequences of random numbers. To use them with a device vector you will need to create a functor which returns a different element of the random generator sequence. The most straightforward way to do this is using a transformation of a counting iterator. A very simple complete example (in this case generating random single precision numbers between 1.0 and 2.0) could look like:

#include <thrust/random.h>
#include <thrust/device_vector.h>
#include <thrust/transform.h>
#include <thrust/iterator/counting_iterator.h>
#include <iostream>

struct prg
{
    float a, b;

    __host__ __device__
    prg(float _a=0.f, float _b=1.f) : a(_a), b(_b) {};

    __host__ __device__
        float operator()(const unsigned int n) const
        {
            thrust::default_random_engine rng;
            thrust::uniform_real_distribution<float> dist(a, b);
            rng.discard(n);

            return dist(rng);
        }
};


int main(void)
{
    const int N = 20;

    thrust::device_vector<float> numbers(N);
    thrust::counting_iterator<unsigned int> index_sequence_begin(0);

    thrust::transform(index_sequence_begin,
            index_sequence_begin + N,
            numbers.begin(),
            prg(1.f,2.f));

    for(int i = 0; i < N; i++)
    {
        std::cout << numbers[i] << std::endl;
    }

    return 0;
}

In this example, the functor prg takes the lower and upper bounds of the random number as an argument, with (0.f,1.f) as the default. Note that in order to have a different vector each time you call the transform operation, you should used a counting iterator initialised to a different starting value.

查看更多
家丑人穷心不美
3楼-- · 2020-02-05 05:26

There are already satisfactory answers to this questions. In particular, the OP and Robert Crovella have dealt with thrust::generate while talonmies has proposed using thrust::transform.

I think there is another possibility, namely, using thrust::for_each, so I'm posting a fully worked example using such a primitive, just for the record.

I'm also timing the different solutions.

THE CODE

#include <iostream>

#include <thrust\host_vector.h>
#include <thrust\generate.h>
#include <thrust\for_each.h>
#include <thrust\execution_policy.h>
#include <thrust\random.h>

#include "TimingCPU.h"

/**************************************************/
/* RANDOM NUMBERS GENERATION STRUCTS AND FUNCTION */
/**************************************************/
template<typename T>
struct rand_01 {
    __host__ T operator()(T& VecElem) const { return (T)rand() / RAND_MAX; }
};

template<typename T>
struct rand_01_for_each {
    __host__ void operator()(T& VecElem) const { VecElem = (T)rand() / RAND_MAX; }
};

template<typename T>
__host__ T rand_01_fcn() { return ((T)rand() / RAND_MAX); }

struct prg
{
    float a, b;

    __host__ __device__
        prg(float _a = 0.f, float _b = 1.f) : a(_a), b(_b) {};

    __host__ __device__
        float operator()(const unsigned int n) const
    {
        thrust::default_random_engine rng;
        thrust::uniform_real_distribution<float> dist(a, b);
        rng.discard(n);

        return dist(rng);
    }
};

/********/
/* MAIN */
/********/
int main() {

    TimingCPU timerCPU;

    const int N = 2 << 18;          
    //const int N = 64;

    const int numIters = 50;

    thrust::host_vector<double>     h_v1(N);
    thrust::host_vector<double>     h_v2(N);
    thrust::host_vector<double>     h_v3(N);
    thrust::host_vector<double>     h_v4(N);

    printf("N = %d\n", N);

    double timing = 0.;
    for (int k = 0; k < numIters; k++) {
        timerCPU.StartCounter();
        thrust::transform(thrust::host, h_v1.begin(), h_v1.end(), h_v1.begin(), rand_01<double>());
        timing = timing + timerCPU.GetCounter();
    }
    printf("Timing using transform = %f\n", timing / numIters);

    timing = 0.;
    for (int k = 0; k < numIters; k++) {
        timerCPU.StartCounter();
        thrust::counting_iterator<unsigned int> index_sequence_begin(0);
        thrust::transform(index_sequence_begin,
            index_sequence_begin + N,
            h_v2.begin(),
            prg(0.f, 1.f));
        timing = timing + timerCPU.GetCounter();
    }
    printf("Timing using transform and internal Thrust random generator = %f\n", timing / numIters);

    timing = 0.;
    for (int k = 0; k < numIters; k++) {
        timerCPU.StartCounter();
        thrust::for_each(h_v3.begin(), h_v3.end(), rand_01_for_each<double>());
        timing = timing + timerCPU.GetCounter();
    }
    timerCPU.StartCounter();
    printf("Timing using for_each = %f\n", timing / numIters);

    //std::cout << "Values generated: " << std::endl;
    //for (int k = 0; k < N; k++)
    //  std::cout << h_v3[k] << " : ";
    //std::cout << std::endl;

    timing = 0.;
    for (int k = 0; k < numIters; k++) {
        timerCPU.StartCounter();
        thrust::generate(h_v4.begin(), h_v4.end(), rand_01_fcn<double>);
        timing = timing + timerCPU.GetCounter();
    }
    timerCPU.StartCounter();
    printf("Timing using generate = %f\n", timing / numIters);

    //std::cout << "Values generated: " << std::endl;
    //for (int k = 0; k < N; k++)
    //  std::cout << h_v4[k] << " : ";
    //std::cout << std::endl;

    //std::cout << "Values generated: " << std::endl;
    //for (int k = 0; k < N * 2; k++)
    //  std::cout << h_v[k] << " : ";
    //std::cout << std::endl;

    return 0;
}

On a laptop Core i5 platform, I had the following timings

N = 2097152
Timing using transform = 33.202298
Timing using transform and internal Thrust random generator = 264.508662
Timing using for_each = 33.155237
Timing using generate = 35.309399 

The timings are equivalent, apart from the second one which uses Thrust's internal random number generator instead of rand().

Please, note that, differently from the other solutions, the one thrust::generate is somewhat more rigid since the function used to generate the random numbers cannot have input parameters. So, for example, it is not possible to scale the input arguments by a constant.

查看更多
Root(大扎)
4楼-- · 2020-02-05 05:29

The approach suggested by @talonmies has a number of useful characteristics. Here's another approach that mimics the example you quoted:

#include <thrust/host_vector.h>
#include <thrust/generate.h>
#include <iostream>
#define DSIZE 5


__host__ static __inline__ float rand_01()
{
    return ((float)rand()/RAND_MAX);
}

int main(){
  thrust::host_vector<float> h_1(DSIZE);

  thrust::generate(h_1.begin(), h_1.end(), rand_01);
  std::cout<< "Values generated: " << std::endl;
  for (unsigned i=0; i<DSIZE; i++)
    std::cout<< h_1[i] << " : ";
  std::cout<<std::endl;
return 0;
}

similar to the example you quoted, this uses rand(), and therefore can only be used to generate host vectors. Likewise it will produce the same sequence each time unless you re-seed rand() appropriately.

查看更多
Rolldiameter
5楼-- · 2020-02-05 05:35

It might not be a direct answer to your question but, cuRand library is quite powerful in this concept. You may both generate random numbers at GPU and CPU, and it contains many distribution functions (normal distribution etc).

Search for the title: "An NVIDIA CURAND implementation" on this link: http://adnanboz.wordpress.com/tag/nvidia-curand/

//Create a new generator
curandCreateGenerator(&m_prng, CURAND_RNG_PSEUDO_DEFAULT);
//Set the generator options
curandSetPseudoRandomGeneratorSeed(m_prng, (unsigned long) mainSeed);
//Generate random numbers
curandGenerateUniform(m_prng, d_randomData, dataCount);

One note is that, do not generate the generator again and again, it makes some precalculations. Calling curandGenerateUniform is quite fast and produces values between 0.0 and 1.0.

查看更多
登录 后发表回答