【问题标题】:CUDA cudaMemcpy Struct of ArraysCUDA cudaMemcpy 数组结构
【发布时间】:2015-10-14 09:18:35
【问题描述】:

我想在我的项目中清理 CUDA 内核的参数。


现在,一个内核需要 3 个uint32_t 数组,这导致代码非常难看:(id 表示全局线程 id,valX 是任意值)

__global__ void some_kernel(uint32_t * arr1, uint32_t * arr2, uint32_t * arr3){arr1[id] = val1; arr2[id] = val2; arr3[id] = val3;}

我想用一个结构来围绕所有这些数组:

typedef struct S{uint_32_t arr1, uint_32_t arr2, uint_32_t arr3, uint32_t size} S;

其中 size 表示结构内每个 arrX 的长度。

我想要的,是这样的:

__global__ void some_kernel(S * s){s->arr1[id] = val1; s->arr2[id] = val2; s->arr3[id] = val3;}

对于这样的结构,对应的 cudaMalloc 和 cudaMemcpy 会是什么样子? 这是否有任何性能缺陷,我还没有看到?

提前致谢!

【问题讨论】:

  • 您是否尝试过cudaMemcpy(dst, src, number_of_Ss * sizeof(S), cudaMemcpyHostToDevice)dstsrcS*s 和number_of_Ss 是您要复制的Ss 的数量?
  • 是的,我得到了这个:test.cu(27): 错误:不存在从“S”到“const void *”的合适转换函数......就在内存复制中
  • 为什么不直接按值传递结构?不需要 cudaMalloc 或 cudaMemcpy。
  • 创建一个包含指针的结构。 cudaMalloc 每个指针。按值传递结构。如果您不理解这一点,那么我认为您需要修改 C++ 中的指针、引用和值。 CUDA 在概念上有点复杂,但在尝试编写 CUDA 代码之前,您需要彻底了解 C 或 C++。你的第一个 C++ 程序不应该也是你的第一个 CUDA 程序。
  • 我不建议将设备内存分配和释放放在构造函数和析构函数中,除非您在管理范围方面非常小心。它可能导致一些非常难以诊断的运行时错误

标签: c++ c arrays struct cuda


【解决方案1】:

您至少有两个选择。一个很好的选择是 talonmies 的 already given,但我会向您介绍“艰苦学习”的方法。

首先,你的结构定义:

typedef struct S {
    uint32_t *arr1;
    uint32_t *arr2;
    uint32_t *arr3; 
    uint32_t size;
} S;

...和内核定义(带有一些全局变量,但您不需要遵循该模式):

const int size = 10000;

__global__ void some_kernel(S *s)
{
    int id = blockIdx.x * blockDim.x + threadIdx.x;
    if (id < size)
    {
        s->arr1[id] = 1; // val1
        s->arr2[id] = 2; // val2
        s->arr3[id] = 3; // val3
    }
}

请注意,if 可以保护您免于越界。

接下来,我们提供了一些准备数据、执行内核并打印结果的函数。第一部分是数据分配:

uint32_t *host_arr1, *host_arr2, *host_arr3;
uint32_t *dev_arr1, *dev_arr2, *dev_arr3;

// Allocate and fill host data
host_arr1 = new uint32_t[size]();
host_arr2 = new uint32_t[size]();
host_arr3 = new uint32_t[size]();

// Allocate device data   
cudaMalloc((void **) &dev_arr1, size * sizeof(*dev_arr1));
cudaMalloc((void **) &dev_arr2, size * sizeof(*dev_arr2));
cudaMalloc((void **) &dev_arr3, size * sizeof(*dev_arr3));

// Allocate helper struct on the device
S *dev_s;
cudaMalloc((void **) &dev_s, sizeof(*dev_s));

没什么特别的,你只需分配三个数组和结构。看起来更有趣的是如何处理将此类数据复制到设备中:

// Copy data from host to device
cudaMemcpy(dev_arr1, host_arr1, size * sizeof(*dev_arr1), cudaMemcpyHostToDevice);
cudaMemcpy(dev_arr2, host_arr2, size * sizeof(*dev_arr2), cudaMemcpyHostToDevice);
cudaMemcpy(dev_arr3, host_arr3, size * sizeof(*dev_arr3), cudaMemcpyHostToDevice);

// NOTE: Binding pointers with dev_s
cudaMemcpy(&(dev_s->arr1), &dev_arr1, sizeof(dev_s->arr1), cudaMemcpyHostToDevice);
cudaMemcpy(&(dev_s->arr2), &dev_arr2, sizeof(dev_s->arr2), cudaMemcpyHostToDevice);
cudaMemcpy(&(dev_s->arr3), &dev_arr3, sizeof(dev_s->arr3), cudaMemcpyHostToDevice);

除了您注意到的普通数组副本之外,还需要将它们与结构“绑定”。为此,您需要传递指针的地址。结果,只有这些指针被复制。

下一次内核调用,再次将数据复制回主机并打印结果:

// Call kernel
some_kernel<<<10000/256 + 1, 256>>>(dev_s); // block size need to be a multiply of 256

// Copy result to host:
cudaMemcpy(host_arr1, dev_arr1, size * sizeof(*host_arr1), cudaMemcpyDeviceToHost);
cudaMemcpy(host_arr2, dev_arr2, size * sizeof(*host_arr2), cudaMemcpyDeviceToHost);
cudaMemcpy(host_arr3, dev_arr3, size * sizeof(*host_arr3), cudaMemcpyDeviceToHost);

// Print some result
std::cout << host_arr1[size-1] << std::endl;
std::cout << host_arr2[size-1] << std::endl;
std::cout << host_arr3[size-1] << std::endl;

请记住,在任何严肃的代码中,您都应该始终检查来自 CUDA API 调用的错误。

【讨论】:

  • 如果先在主机内存中构建设备结构,然后将其复制到dev_s,则可以将// NOTE: Binding pointers with dev_s 部分中的三个内存副本替换为单个memcpy。这将变得更加简单和快捷
猜你喜欢
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 2011-10-12
相关资源
最近更新 更多