Struct 将带有函数指针的结构复制到设备

Struct 将带有函数指针的结构复制到设备,struct,cuda,gpu,Struct,Cuda,Gpu,我有一个包含线性函数参数的结构,以及函数本身。我要做的是将这个结构复制到设备上,然后计算线性函数。下面的例子没有意义,但足以描述我遇到的困难: struct model { double* params; double (*func)(double*, double); }; 我不知道如何将这个结构复制到设备上 以下是我的职能: 初始化函数 评价函数 实际功能 在内核中调用的函数 内核本身 \uuuuu全局\uuuuuu无效内核(双*数组,模型*d\u线性模型,int N) { int idx

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

struct model
{
double* params;
double (*func)(double*, double);
};
我不知道如何将这个结构复制到设备上

以下是我的职能:

初始化函数 评价函数 实际功能 在内核中调用的函数 内核本身
\uuuuu全局\uuuuuu无效内核(双*数组,模型*d\u线性模型,int N)
{
int idx=blockIdx.x*blockDim.x+threadIdx.x;
if(idx
我知道如何将数组从主机复制到设备,但我不知道如何为这个包含函数的具体结构执行此操作

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);
int block_size=4;
int n_blocks=n_数组/块大小+(n_数组%block大小==0?0:1);
内核(设备_阵列、d_线性_模型、N_阵列);

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

  • 设备函数指针的使用
  • “深度复制”操作(在
    模型
    结构中嵌入的
    参数
    指针上)
  • 这两个主题已在其他问题中介绍。例如,讨论深度复制操作-当数据结构具有指向其他数据的嵌入式指针时。并链接到有关设备函数指针使用的各种资源

    但我会继续为你的案例提供一个可能的解决方案。您的大多数代码都可以按原样使用(至少出于演示目的)。如前所述,您的
    模型
    结构将面临两个挑战:

    struct model
    {
    double* params;  // requires a "deep copy" operation
    double (*func)(double*, double);  // requires special handling for device function pointers
    };
    
    因此,尽管您的大多数代码都是可用的,但“init”函数不是。这可能适用于主机实现,但不适用于设备实现

    深度复制操作要求我们复制整个结构,再加上单独复制嵌入指针指向的数据,再加上单独复制或“修复”嵌入指针本身

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

    下面是一个基于您所展示内容的工作示例,演示了上述两个概念。我还没有创建“device init”函数,但所有需要创建的代码都在
    main
    函数中。一旦你掌握了这些概念,你可以从下面的主函数中提取任何你想要的代码,如果你想创建一个的话,你可以将它制作成你的“device init”函数

    下面是一个成功的例子:

    $ 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
    $
    
    $cat t968.cu
    #包括
    #定义NUM_参数2
    #定义ARR_大小1
    #定义nTPB 256
    结构模型
    {
    双*参数;
    双(*func)(双*,双);
    };
    //结构模型的init函数--不用于设备操作
    __host_uuuu void model_uinit(model*m,double*参数,double(*func)(double*,double))
    {
    如果(m)
    {
    m->params=params;
    m->func=func;
    }
    }
    __设备?双模型?评估(模型*m,双x)
    {
    如果(m)
    {
    返回m->func(m->params,x);
    }
    返回0.0;
    }
    __主机设备双线性函数(双*参数,双x)
    {
    返回参数[0]+参数[1]*x;
    }
    __设备双计算(模型*d线性模型)
    {
    收益模型(d_线性模型,1.0);
    }
    __全局无效内核(双*数组,模型*d\u线性模型,int N)
    {
    int idx=blockIdx.x*blockDim.x+threadIdx.x;
    if(idxparams),&d_params,sizeof(double*),cudaMemcpyHostToDevice);
    //运行测试
    双*d_数组,*h_数组;
    cudaMalloc(&d_数组,ARR_大小*sizeof(双精度));
    h_数组=新的双精度[ARR_SIZE];
    对于(int i=0;iStd::Couth

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

  • 设备函数指针的使用
  • “深度复制”操作(在
    模型
    结构中嵌入的
    参数
    指针上)
  • 这两个主题已在其他问题中讨论。例如,讨论深度复制操作-当数据结构嵌入指向其他数据的指针时。以及有关设备函数指针使用的各种资源的链接

    但是我会继续为您发布的案例提供一个可能的解决方案。您的大多数代码都是可用的(至少用于演示目的)。正如前面提到的,您的
    模型
    
    __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);
      }
    }
    
      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);
    
    struct model
    {
    double* params;  // requires a "deep copy" operation
    double (*func)(double*, double);  // requires special handling for device function pointers
    };
    
    $ 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
    $