CUDA:被CUDA内存模型和内存分配所困惑

时间:2015-10-08 11:05:53

标签: memory cuda

对于那些在将来感到困惑的人,TLDR:

  • Malloc(从CPU线程调用时)在主机上分配内存。你已经知道了,这里没什么不同。

  • Malloc(从GPGPU [device]线程调用时)在设备或GPGPU内存上分配内存。这基本上就是你想在CUDA内核中分配内存的目的。

  • CudaMalloc有点奇怪,它在设备上分配内存,但从(例如int main())主机功能调用。因此,这是从CPU上执行的线程调用的,但是在设备或GPGPU上分配内存。当发生这种情况时,我并不完全明白指针指向的内容。

问题

我是CUDA的新手。我目前对CUDA中内存分配的工作方式感到困惑。

我可以提出许多问题组合以获得深入的理解,但为了简单起见,我只会问一个问题。

我正在并行化我编写的代码以使用C ++ 11线程。 (实际上,它已经并行化了,我只是从CPU线程转移到GPU线程。)

我有一个cuda内核函数。它看起来像这样:

__global__
void cuda_kernel(int N)
{
    std::vector<double> vec;
    for(int i = 0; i < N; ++ i) vec.push_back(0.0);
}

然而,这是不允许的。

旁注:

我不知道为什么。 我不非常关心为什么,但如果您想告诉我,那么我将很乐意阅读您提供的信息。知道原因总是好的,但这不是我的主要问题 。 (很多时候我遇到的问题是有人回答他们想要看到的问题,而不是实际提出的问题,所以这就是为什么我提出这个评论。请“阅读完整的问题”是我的高中数学老师经常说的!但你可能不关心这一点。)

请注意,有些人似乎对此代码包含的原因感到困惑。这只是为了证明我意识到我不能这样做。我最初使用std::vector编写代码,但现在我意识到这不适用于CUDA系统,我不再使用C ++和向量,我正在使用C和(希望当我更好地理解它)“老学校风格“记忆分配 - 我现在认为它涉及cudaMalloc()malloc() - 但是对于使用哪一种感到困惑。

返回问题

我不知道如何在CUDA内核函数/ CUDA GPGPU设备上运行的线程中使用动态内存。这就是我想知道的:

我如何......

  • 在GPGPU设备上分配要由GPGPU使用的内存。
  • 完成后释放/取消分配此内存。
  • 访问此内存的读/写。

不要担心数据争用,因为......

(这是一个类似的过程,解释了原因。)

我的并行化过程就是我称之为内在并行化或平凡可并行化的过程。我不可能获得数据争用/内存损坏,因为所有内存块都独立于其他内存。可以认为这类似于向量加法问题。当添加vec A = B + C时,所有组件都是独立的,因此该过程可以简单地并行化。

再次注意我没有做任何与矢量有关的事情。这只是一个例子,可以帮助解释我的代码的作用。不,它不会将向量一起添加,但它确实以类似的方式工作,因为块分配的内存的元素之间没有交叉通信。我的意思是,没有处理器读取和写入超过它们自己唯一分配的内存区域。如果您仍然不明白,那么只需忽略数据损坏或数据争用的可能性。他们不可能发生。

返回问题

如果有人可以帮助我,我将不胜感激。当我的理解得到改善时,我可能会在以后发布更复杂的问题。谢谢。

注意:

我删除了C ++标记并将其替换为C,因为这实际上是一个C问题而不是C ++标记。

注2:

这是一个极其狭隘的问题。我想知道如何做3件事。那些是:

  • 在GPGPU设备上分配要由GPGPU使用的内存。
  • 完成后释放/取消分配此内存。
  • 访问此内存的读/写。

3 个答案:

答案 0 :(得分:3)

设备代码不支持通过C ++标准库(例如std::vector)分配内存。如果要在内核中进行动态内存分配,则必须使用mallocfree

__global__
void cuda_kernel(int N)
{
    double *vec = malloc(N * sizeof(double));
    for(int i = 0; i < N; ++ i) vec[i] = 0.0;
    free(vec);
}

可以在一个内核中使用malloc,在另一个内核中使用free - 内存在调用之间保持不变。设备代码中malloc的分配来自设备堆,设备堆是设备内存的一部分。 cudaMalloc(可从主机调用)可以获得更多内存,而不是malloc(可从设备调用)。

答案 1 :(得分:3)

您似乎忽略了更典型的选项:不要编写需要分配内存的内核。相反,调用者应将内存传递到内核;例如传入CudaMallocthrust::device_vector获得的指针。

当然,这个内存需要在所有线程之间共享;使这个缓冲区对每个人都足够,然后每个线程使用它的线程和块索引来确定缓冲区的哪个部分属于它。

例如,

__global__
void kernel(int N, double *vec_all)
{
    int index = threadIdx.x + blockDim.x * blockIdx.x;
    double *vec = vec_all + N * index;
    for(int i = 0; i < N; ++i) { vec[i] = 0.0; }
}

答案 2 :(得分:1)

首先,内核无法正常工作的原因是您正在使用C ++标准库。 CUDA C不支持此功能。因此,您不能使用std :: vector或其他STL类型。

您提出的问题非常基本,您应该能够轻松找到这些信息。试过查找还是直接来这里?

查看CUDA C programming guide。它包含的示例几乎完全符合您的要求。