如何在CUDA C中生成每次运行时具有不同种子的随机数?
我正在研究一个随机过程,每次运行程序时,我都想在CUDA内核中生成不同的序列if随机数。 这与我们在C++中通过声明所做的类似 种子=时间(空) 然后是srand(seed) 兰特() 我可以通过内核将种子从主机传递到设备,但是如何在CUDA C中生成每次运行时具有不同种子的随机数?,cuda,nvidia,gpu,Cuda,Nvidia,Gpu,我正在研究一个随机过程,每次运行程序时,我都想在CUDA内核中生成不同的序列if随机数。 这与我们在C++中通过声明所做的类似 种子=时间(空) 然后是srand(seed) 兰特() 我可以通过内核将种子从主机传递到设备,但是 这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有一个不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?在每次运行时使用不同的种子应该很简单。确切的方法取决于您使用的生成器,但是如果您使用的是
这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有一个不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?在每次运行时使用不同的种子应该很简单。确切的方法取决于您使用的生成器,但是如果您使用的是其中一个生成器,那么您可以将time\u t从time(NULL)转换为64位整数,并将其传递给种子函数 如果从内核调用生成器,则需要将此种子作为内核参数或通过
\uuu设备\uu
变量传入。然后,可以使用偏移量curand\u init()
或使用skip\u ahead()
获得不同的子序列
如果您有一个特定的生成器,但该生成器不起作用,请发布更多信息。您不需要传递随机种子数组,但是,当您使用cuRAND库时,可以正确设置
cuRAND\u init
的序号参数。例如[免责声明:这是一个未经测试的功能]
__global__ void generate_random_numbers(float* numbers, unsigned long seed, int Np) {
int i = threadIdx.x + blockIdx.x * blockDim.x;
if (i < Np) {
curandState state;
curand_init(seed, i, 0, &state);
numbers[i] = curand_uniform(&state);
}
}
编辑
根据Roger Dahl的评论,我对生成131072
元素数组的四种不同可能性进行了比较(开普勒K20c):
861ms
李>
852ms
李>
866ms
李>
2556ms
李>
我希望我已经正确理解了罗杰·达尔提出的性能问题
#include <stdio.h>
#include <curand.h>
#include <curand_kernel.h>
#define DSIZE 8192*16
#define nTPB 256
/***********************/
/* CUDA ERROR CHECKING */
/***********************/
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
/*************************/
/* CURAND INITIALIZATION */
/*************************/
__global__ void initCurand(curandState *state, unsigned long seed){
int idx = threadIdx.x + blockIdx.x * blockDim.x;
curand_init(seed, idx, 0, &state[idx]);
}
__global__ void testrand1(curandState *state, float *a){
int idx = threadIdx.x + blockIdx.x * blockDim.x;
a[idx] = curand_uniform(&state[idx]);
}
__global__ void testrand2(unsigned long seed, float *a){
int idx = threadIdx.x + blockIdx.x * blockDim.x;
curandState state;
curand_init(seed, idx, 0, &state);
a[idx] = curand_uniform(&state);
}
/********/
/* MAIN */
/********/
int main() {
int n_iter = 20;
curandState *devState; gpuErrchk(cudaMalloc((void**)&devState, DSIZE*sizeof(curandState)));
float *d_a; gpuErrchk(cudaMalloc((void**)&d_a, DSIZE*sizeof(float)));
float time;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
for (int i=0; i<n_iter; i++) {
initCurand<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, 1);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Elapsed time for separate kernels: %3.1f ms \n", time);
cudaEventRecord(start, 0);
for (int i=0; i<n_iter; i++) {
testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Elapsed time for single kernels: %3.1f ms \n", time);
cudaEventRecord(start, 0);
for (int i=0; i<n_iter; i++) {
initCurand<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, 1);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Elapsed time for separate kernels with multiple random number generation: %3.1f ms \n", time);
cudaEventRecord(start, 0);
for (int i=0; i<n_iter; i++) {
testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Elapsed time for single kernels for multiple random number generation: %3.1f ms \n", time);
getchar();
}
GTX570上的输出:
单独内核的运行时间:957.2毫秒
单内核运行时间:947.7毫秒
生成多个随机数的独立内核的运行时间:964.6毫秒
生成多个随机数的单内核运行时间:2839.0毫秒
性能与K20c大致相同。您可以创建多个全局函数来初始化和生成随机数。或者创建一个循环来遍历全局函数 例子: 对于(int rns=0;rns<5;rns++){//too种子“循环”次数
init << < N, 10 >> > (devState, time(0));
gpuErrchk(cudaMalloc((void**)&gpu_no, N * sizeof(double))); // allocate memory for random numbers on device/GPU
//rndn << < N, 10 >> > (devState, gpu_no);//invoke kernel to launch the random numbers
gpuErrchk(cudaMemcpy(cpu_no, gpu_no, N * sizeof(double), cudaMemcpyDeviceToHost))
} cout << "the transition matrix " << ++generate << " seed generation is: " << init << endl;
init>>(devState,时间(0));
gpuErrchk(cudamaloc((void**)和gpu_no,N*sizeof(double));//为设备/gpu上的随机数分配内存
//rndn>>(devState,gpu_no);//调用内核以启动随机数
gpuerchk(cudaMemcpy(cpu_编号,gpu_编号,N*sizeof(双精度),cudaMemcpyDeviceToHost))
}到目前为止,你们的代码是什么?我目前正在使用curand_uniform()对于随机数生成,但它每次运行时都调用同一组随机数,我可以通过内核将种子从主机传递到设备,但这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?您可以使用单个主种子,然后根据线程索引偏移到序列中。请注意,仅对同一个生成器使用不同的种子在统计上并不可靠,您应该使用单个种子,然后偏移到序列中以获得不同的子序列。这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都具有不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?没有,可以传入单个种子,然后在序列中使用偏移量。无论如何,您不应该只使用不同的种子,这种方法容易导致序列之间的相关性。处理prng种子的不同方法会对性能产生影响。从curand文档中的性能说明部分:对curand_init()的调用比对curand()或curand_uniform()的调用慢。到curand_init()的大偏移量比小偏移量花费更多的时间。保存和恢复随机生成器状态要比反复重新计算开始状态快得多。@RogerDahl我已经编辑了我的答案。我希望我正确理解了您提出的性能问题。感谢您调查性能问题。我认为从文档中的注释中得到的主要信息是,如果可能的话,将PRNG的种子转移到循环之外可能是一个好主意。因此,如果您正在运行一个模拟,其中您一直启动一个生成随机数的内核,那么您将为每个线程创建一个运行curand_init()
的内核,并保存状态。当应用程序启动时,您只运行该内核一次。然后,在循环中,您只需继续恢复和保存状态。@RogerDahl感谢您将自己的结果添加到我的答案中。我认为上述结果与您最后的评论一致,在这种情况下#3的性能明显优于#4。
init << < N, 10 >> > (devState, time(0));
gpuErrchk(cudaMalloc((void**)&gpu_no, N * sizeof(double))); // allocate memory for random numbers on device/GPU
//rndn << < N, 10 >> > (devState, gpu_no);//invoke kernel to launch the random numbers
gpuErrchk(cudaMemcpy(cpu_no, gpu_no, N * sizeof(double), cudaMemcpyDeviceToHost))
} cout << "the transition matrix " << ++generate << " seed generation is: " << init << endl;