将带有函数指针的struct复制到device

时间:2015-11-03 14:36:26

标签: struct cuda gpu

我有一个包含线性函数参数的结构,以及函数本身。我想要做的是将此结构复制到设备,然后评估线性函数。以下示例没有意义,但足以描述我遇到的困难:

struct model
{
double* params;
double (*func)(double*, double);
};

我不知道如何将此结构复制到设备。

以下是我的功能:

初始化功能

// init function for struct model
__host__ void model_init(model* m, double* params, double(*func)(double*,double))
{
if(m)
{
    m->params = params;
    m->func = func;
}
}

评估功能

__device__ double model_evaluate(model* m, double x)
{
if(m)
{
    return m->func(m->params, x);
}
return 0.0;
}

实际功能

__host__ __device__ double linear_function(double* params, double x)
{
return params[0] + params[1] * x;
}

内核中调用的函数

__device__ double compute(model *d_linear_model)
{
    return model_evaluate(d_linear_model,1.0);
}

内核本身

__global__ void kernel(double *array, model *d_linear_model, int N)
 {
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  if (idx < N)
  {
    array[idx] = compute(d_linear_model);
  }
}

我知道如何将数组从主机复制到设备但我不知道如何为包含函数的这个具体结构执行此操作。

main中的内核调用如下所示:

  int block_size = 4;
  int n_blocks = N_array/block_size + (N_array % block_size == 0 ? 0:1);
  kernel<<<n_blocks, block_size>>>(device_array, d_linear_model, N_array);

1 个答案:

答案 0 :(得分:3)

您已经概述了两个我认为比初学者级CUDA编程更困难的项目:

  1. 使用设备功能指针
  2. a&#34;深拷贝&#34;操作(在params结构中的嵌入式model指针上)
  3. 其他问题都涵盖了这两个主题。例如,this question/answer讨论深度复制操作 - 当数据结构嵌入了指向其他数据的指针时。 this question/answer链接到设备函数指针使用的各种资源。

    但我会继续为您发布的案例提供可能的解决方案。您的大部分代码都可以按原样使用(至少出于演示目的)。如前所述,您的model结构将带来两个挑战:

    struct model
    {
    double* params;  // requires a "deep copy" operation
    double (*func)(double*, double);  // requires special handling for device function pointers
    };
    

    因此,尽管您的大部分代码都可以按原样使用,但您的&#34; init&#34;功能不是。这可能适用于主机实现,但不适用于设备实现。

    深度复制操作要求我们复制整体结构,再单独复制嵌入式指针指向的数据, plus 分别复制或&#34; fixup&#34;嵌入式指针本身。

    设备函数指针的使用受到以下事实的限制:我们无法获取主机代码中的实际设备函数指针 - 这在CUDA中是非法的。因此,一种可能的解决方案是使用__device__构造来捕获&#34;设备函数指针,然后在主机代码中执行cudaMemcpyFromSymbol操作,以检索设备函数指针的数值,然后可以以普通方式移动它。

    这是一个基于你所展示的作品的实例,展示了上述两个概念。我还没有创建一个&#34;设备init&#34;函数 - 但执行该操作所需的所有代码都在main函数中。一旦你掌握了概念,你可以从下面的主要功能中获取你想要的任何代码,并将其制作成你的设备初始化&#34;功能,如果你想创建一个。

    这是一个有效的例子:

    $ cat t968.cu
    #include <iostream>
    
    #define NUM_PARAMS 2
    #define ARR_SIZE 1
    #define nTPB 256
    
    struct model
    {
      double* params;
      double (*func)(double*, double);
    };
    
    // init function for struct model -- not using this for device operations
    __host__ void model_init(model* m, double* params, double(*func)(double*,double))
    {
    if(m)
    {
      m->params = params;
      m->func = func;
    }
    }
    
    __device__ double model_evaluate(model* m, double x)
    {
    if(m)
    {
      return m->func(m->params, x);
    }
    return 0.0;
    }
    
    __host__ __device__ double linear_function(double* params, double x)
    {
      return params[0] + params[1] * x;
    }
    
    __device__ double compute(model *d_linear_model)
    {
      return model_evaluate(d_linear_model,1.0);
    }
    
    
    __global__ void kernel(double *array, model *d_linear_model, int N)
     {
      int idx = blockIdx.x * blockDim.x + threadIdx.x;
      if (idx < N)
      {
        array[idx] = compute(d_linear_model);
      }
    }
    
    __device__ double (*linear_function_ptr)(double*, double) = linear_function;
    
    int main(){
    
      // grab function pointer from device code
      double (*my_fp)(double*, double);
      cudaMemcpyFromSymbol(&my_fp, linear_function_ptr, sizeof(void *));
      // setup model
      model my_model;
      my_model.params = new double[NUM_PARAMS];
      my_model.params[0] = 1.0;
      my_model.params[1] = 2.0;
      my_model.func = my_fp;
      // setup for device copy of model
      model *d_model;
      cudaMalloc(&d_model, sizeof(model));
      // setup "deep copy" for params
      double *d_params;
      cudaMalloc(&d_params, NUM_PARAMS*sizeof(double));
      cudaMemcpy(d_params, my_model.params, NUM_PARAMS*sizeof(double), cudaMemcpyHostToDevice);
      // copy model to device
      cudaMemcpy(d_model, &my_model, sizeof(model), cudaMemcpyHostToDevice);
      // fixup device params pointer in device model
      cudaMemcpy(&(d_model->params), &d_params, sizeof(double *), cudaMemcpyHostToDevice);
    
      // run test
      double *d_array, *h_array;
      cudaMalloc(&d_array, ARR_SIZE*sizeof(double));
      h_array = new double[ARR_SIZE];
      for (int i = 0; i < ARR_SIZE; i++) h_array[i] = i;
      cudaMemcpy(d_array, h_array, ARR_SIZE*sizeof(double), cudaMemcpyHostToDevice);
      kernel<<<(ARR_SIZE+nTPB-1)/nTPB,nTPB>>>(d_array, d_model, ARR_SIZE);
      cudaMemcpy(h_array, d_array, ARR_SIZE*sizeof(double), cudaMemcpyDeviceToHost);
      std::cout << "Results: " << std::endl;
      for (int i = 0; i < ARR_SIZE; i++) std::cout << h_array[i] << " ";
      std::cout << std::endl;
      return 0;
    }
    $ nvcc -o t968 t968.cu
    $ cuda-memcheck ./t968
    ========= CUDA-MEMCHECK
    Results:
    3
    ========= ERROR SUMMARY: 0 errors
    $
    

    为简洁起见,我放弃了proper cuda error checking(相反,我已经使用cuda-memcheck运行代码来证明它没有运行时错误)但我建议正确的错误检查您在使用代码时遇到任何问题。