Random 不确定的?

Random 不确定的?,random,cuda,random-seed,Random,Cuda,Random Seed,我想以确定性的方式在CUDA设备上生成伪随机数,如果我运行两次程序,我希望得到完全相同的结果,因为程序使用了硬编码种子。以下是nvidia提供的示例: 我期待的正是所描述的行为 但我确实得到了不同的结果,多次运行完全相同的代码。有没有一种方法可以像我描述的那样,以确定性的方式获得伪随机数 以下示例代码显示了我的问题: #include <iostream> #include <cuda.h> #include <curand_kernel.h> __glo

我想以确定性的方式在CUDA设备上生成伪随机数,如果我运行两次程序,我希望得到完全相同的结果,因为程序使用了硬编码种子。以下是nvidia提供的示例: 我期待的正是所描述的行为

但我确实得到了不同的结果,多次运行完全相同的代码。有没有一种方法可以像我描述的那样,以确定性的方式获得伪随机数

以下示例代码显示了我的问题:

#include <iostream>

#include <cuda.h>
#include <curand_kernel.h>

__global__ void setup_kernel(curandState *state)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;
  curand_init(123456, id, 0, &state[id]);
}

__global__ void draw_numbers(curandState *state, float* results)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;
  // Copy state
  curandState localState = state[id % 1024];
  // Generate random number
  results[id] = curand_uniform(&localState);
  // Copy back state
  state[id % 1024] = localState;
}

int main(int argc, char* argv[])
{
  // Setup
  curandState* dStates;
  cudaMalloc((void **) &dStates, sizeof(curandState) * 1024);
  setup_kernel<<<1024, 1>>>(dStates);

  // Random numbers
  float* devResults;
  cudaMalloc((void **) &devResults, sizeof(float) * 16 * 1024);
  float *hostResults = (float*) calloc(16 * 1024, sizeof(float));

  // Call draw random numbers
  draw_numbers<<<1024, 16>>>(dStates, devResults);

  // Copy results
  cudaMemcpy(hostResults, devResults, 16 * 1024 * sizeof(float), cudaMemcpyDeviceToHost);

  // Output number 12345
  ::std::cout << "12345 is: " << hostResults[12345] << ::std::endl;

  return 0;
}
正如我所说的,在这个例子中,我希望相同的输出是原来的三倍。

curand\u uniform
确实取决于它所提供的状态。 多亏了Robert Crovella的评论,我现在明白了错误在于依赖线程执行顺序。如果多次调用draw_numbers内核,不重用状态将导致相同的“随机”数,这对我来说也不是一个选项

我的猜测是,在我的例子中,最好的解决方案是只启动1024个线程(设置了最多curandState),并在每个线程中生成多个随机数(在我的示例中为16个/线程)。通过这种方式,我在程序内的多个调用中收到不同的随机数,但每次启动程序都会收到相同的数字

更新代码:

#include <iostream>

#include <cuda.h>
#include <curand_kernel.h>

__global__ void setup_kernel(curandState *state)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;
  curand_init(123456, id, 0, &state[id]);
}

__global__ void draw_numbers(curandState *state, float* results, int runs)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;

  // Copy state
  curandState localState = state[id];

  // Generate random numbers
  for (int i = 0; i < runs; ++i)
  {
    results[id + i * 1024] = curand_uniform(&localState);
  }

  // Copy back state
  state[id] = localState;
}

int main(int argc, char* argv[])
{
  // Setup
  curandState* dStates;
  cudaMalloc((void **) &dStates, sizeof(curandState) * 1024);
  setup_kernel<<<1024, 1>>>(dStates);

  // Random numbers
  float* devResults;
  cudaMalloc((void **) &devResults, sizeof(float) * 16 * 1024);
  float *hostResults = (float*) calloc(16 * 1024, sizeof(float));

  // Call draw random numbers
  draw_numbers<<<16, 64>>>(dStates, devResults, 16);
  // Copy results
  cudaMemcpy(hostResults, devResults, 16 * 1024 * sizeof(float), cudaMemcpyDeviceToHost);

  // Output number 12345
  ::std::cout << "12345 is " << hostResults[12345];

  // Call draw random numbers (again)
  draw_numbers<<<16, 64>>>(dStates, devResults, 16);
  // Copy results
  cudaMemcpy(hostResults, devResults, 16 * 1024 * sizeof(float), cudaMemcpyDeviceToHost);

  // Output number 12345 again
  ::std::cout << " and " << hostResults[12345] << ::std::endl;

  return 0;
}

这正符合我的使用情况。

我猜您是在一个“小型”GPU上运行此功能的。但这既不存在,也不存在。CUDA对线程执行不施加任何命令。让我们看看这对你的案子意味着什么。由于您在多个线程之间重用curand状态,并且使用特定状态的每个线程都在读取和写入该状态,因此假设使用特定状态的线程(例如,
id
为0、1024、2048等的线程)在不同的运行顺序下执行?然后,特定线程的结果会因运行而异。您可以通过不以这种方式重用状态来解决这个问题。作为一个简单的证明,请注释掉
draw_numbers
内核代码的最后一行,我认为您将看到一致的结果,即从运行到运行。在CUDA中,如果您编写的代码依赖于特定线程执行顺序的正确性,作为一个程序员,你不能执行这个命令,你的代码被破坏了,不管它产生什么结果。
#include <iostream>

#include <cuda.h>
#include <curand_kernel.h>

__global__ void setup_kernel(curandState *state)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;
  curand_init(123456, id, 0, &state[id]);
}

__global__ void draw_numbers(curandState *state, float* results, int runs)
{
  auto id = threadIdx.x + blockIdx.x * blockDim.x;

  // Copy state
  curandState localState = state[id];

  // Generate random numbers
  for (int i = 0; i < runs; ++i)
  {
    results[id + i * 1024] = curand_uniform(&localState);
  }

  // Copy back state
  state[id] = localState;
}

int main(int argc, char* argv[])
{
  // Setup
  curandState* dStates;
  cudaMalloc((void **) &dStates, sizeof(curandState) * 1024);
  setup_kernel<<<1024, 1>>>(dStates);

  // Random numbers
  float* devResults;
  cudaMalloc((void **) &devResults, sizeof(float) * 16 * 1024);
  float *hostResults = (float*) calloc(16 * 1024, sizeof(float));

  // Call draw random numbers
  draw_numbers<<<16, 64>>>(dStates, devResults, 16);
  // Copy results
  cudaMemcpy(hostResults, devResults, 16 * 1024 * sizeof(float), cudaMemcpyDeviceToHost);

  // Output number 12345
  ::std::cout << "12345 is " << hostResults[12345];

  // Call draw random numbers (again)
  draw_numbers<<<16, 64>>>(dStates, devResults, 16);
  // Copy results
  cudaMemcpy(hostResults, devResults, 16 * 1024 * sizeof(float), cudaMemcpyDeviceToHost);

  // Output number 12345 again
  ::std::cout << " and " << hostResults[12345] << ::std::endl;

  return 0;
}
$ nvcc -std=c++11 curand.cu && ./a.out && ./a.out && ./a.out
12345 is 0.164181 and 0.295907
12345 is 0.164181 and 0.295907
12345 is 0.164181 and 0.295907