我正在用CUDA和C ++编写一个业余raytracer,但遇到了一个我无法得到答案的问题。我已经编写了CPU和GPU代码,以便可以在具有或不具有CUDA功能的设备的机器上执行。但是,这在以下意义上导致了一些代码重复:
一小部分功能需要随机数生成,这可以通过主机上的stdlib
和设备上的curand
来实现。我很想拥有__host__ __device__
函数,该函数采用Sampler
结构,可以在主机上调用rand()
或在设备上调用curand_uniform()
。我已经尝试了一些方法,但是无法使程序进行编译-编译器抱怨无法从__device__
代码中调用__host__
函数,反之亦然。
理想情况下,我希望渲染函数采用Sampler *
,看起来像下面的代码。
谢谢!
struct Sampler {
__host__ virtual float getNextFloat() { return rand() / (RAND_MAX + 1.f); }
};
struct CudaSampler : Sampler {
curandState* p_curandState;
__device__ float getNextFloat() { return curand_uniform(p_curandState); }
};
答案 0 :(得分:1)
您所要求的应该是可能的。我们不想尝试通过__host__
和__device__
分别重载一个函数(不允许),并且我们不想尝试使用继承和虚函数(虚拟函数表将不能在从主机传递到设备的对象中使用。
但是,如果我们避免这些问题,基本思想是使用__CUDA_ARCH__
nvcc macro来区分编译器的主机路径和设备路径,通常遵循建议的here。 / p>
这是一种可能的方法,大致遵循您的概述:
$ cat t34.cu
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <curand_kernel.h>
struct Sampler {
__host__ __device__ float operator()(curandState *s){
#ifdef __CUDA_ARCH__
return curand_uniform(s);
#else
return rand()/(float)RAND_MAX;
#endif
}
};
__global__ void init_rng(curandState *state, size_t n){
size_t idx = threadIdx.x+blockDim.x*blockIdx.x;
if (idx < n)
curand_init(1234, idx, 0, state+idx);
}
__global__ void gpu_sample(Sampler s, curandState *state, size_t n){
size_t idx = threadIdx.x+blockDim.x*blockIdx.x;
if (idx < n)
printf("gpu id: %lu, val: %f\n", idx, s(state+idx));
}
__host__ void cpu_sample(Sampler s){
curandState dummy;
std::cout << "cpu: " << s(&dummy) << std::endl;
}
int main(){
int n = 1;
int nTPB = 256;
curandState *s;
Sampler my_op;
cudaMalloc(&s, n*sizeof(curandState));
init_rng<<<(n+nTPB-1)/nTPB, nTPB>>>(s,n);
gpu_sample<<<(n+nTPB-1)/nTPB, nTPB>>>(my_op, s, n);
cudaDeviceSynchronize();
cpu_sample(my_op);
}
$ nvcc -o t34 t34.cu
$ cuda-memcheck ./t34
========= CUDA-MEMCHECK
gpu id: 0, val: 0.145468
cpu: 0.840188
========= ERROR SUMMARY: 0 errors
$