Генерация случайных чисел с равномерным распределением с использованием Thrust

Мне нужно создать вектор со случайными числами между 0.0 а также 1.0 с помощью Thrust, Единственный задокументированный пример, который я смог найти, дает очень большие случайные числа (thrust::generate(myvector.begin(), myvector.end(), rand).
Я уверен, что ответ прост, но я был бы признателен за любые предложения.

6

Решение

В Thrust есть генераторы случайных чисел, которые вы можете использовать для создания последовательностей случайных чисел. Чтобы использовать их с вектором устройства, вам нужно создать функтор, который возвращает другой элемент последовательности генератора случайных чисел. Самый простой способ сделать это — использовать преобразование счетного итератора. Очень простой законченный пример (в данном случае генерирование случайных чисел одинарной точности между 1,0 и 2,0) может выглядеть следующим образом:

#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;
}

В этом примере функтор prg принимает нижнюю и верхнюю границы случайного числа в качестве аргумента, с (0.f,1.f) по умолчанию. Обратите внимание, что для того, чтобы каждый раз при вызове операции преобразования иметь новый вектор, необходимо использовать итератор подсчета, инициализированный другим начальным значением.

13

Другие решения

Возможно, это не будет прямым ответом на ваш вопрос, но библиотека cuRand достаточно мощная в этой концепции. Вы можете генерировать случайные числа как на GPU, так и на CPU, и он содержит много функций распределения (нормальное распределение и т. Д.).

Ищите заголовок: «Реализация NVIDIA CURAND» по этой ссылке: 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);

Одно замечание заключается в том, что не генерируйте генератор снова и снова, он делает некоторые предварительные расчеты. Вызов curandGenerateUniform довольно быстрый и выдает значения от 0,0 до 1,0.

4

Подход, предложенный @talonmies, имеет ряд полезных характеристик. Вот еще один подход, который имитирует приведенный вами пример:

#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;
}

подобно приведенному вами примеру, он использует rand () и поэтому может использоваться только для генерации векторов хоста. Аналогично, он будет выдавать одну и ту же последовательность каждый раз, если вы не перезапустите rand () соответствующим образом.

2

Уже есть удовлетворительные ответы на эти вопросы. В частности, ОП и Роберт Кровелла имели дело с thrust::generate в то время как talonmies предложил использовать thrust::transform,

Я думаю, что есть еще одна возможность, а именно, использование thrust::for_eachИтак, я публикую полностью проработанный пример с использованием такого примитива, просто для записи.

Я также рассчитываю различные решения.

КОД

#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;
}

На ноутбуке Core i5 Платформа, у меня были следующие сроки

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

Сроки эквивалентны, кроме второго, который использует Thrustвнутренний генератор случайных чисел вместо rand(),

Пожалуйста, обратите внимание, что, в отличие от других решений, один thrust::generate несколько больше жесткий поскольку функция, используемая для генерации случайных чисел, не может иметь входных параметров. Так, например, невозможно масштабировать входные аргументы по константе.

0
По вопросам рекламы [email protected]