CUDA - memcpy2d - 错误的音高

CUDA - memcpy2d - wrong pitch

本文关键字:错误 memcpy2d CUDA      更新时间:2023-10-16

我刚刚开始 CUDA 编程,并尝试执行如下所示的代码。这个想法是将二维数组复制到设备,计算所有元素的总和,然后检索总和(我知道这个算法不是并行化的。事实上,它正在做更多的工作,然后是必要的。然而,这只是作为记忆复制的实践)。

#include<stdio.h>
#include<cuda.h>
#include <iostream>
#include <cutil_inline.h>
#define height 50
#define width 50
using namespace std;
// Device code
__global__ void kernel(float* devPtr, int pitch,int* sum)
{
int tempsum = 0;    
for (int r = 0; r < height; ++r) {
        int* row = (int*)((char*)devPtr + r * pitch);
        for (int c = 0; c < width; ++c) {
             int element = row[c];
             tempsum = tempsum + element;
        }
    }
*sum = tempsum;
}
//Host Code
int main()
{
int testarray[2][8] = {{4,4,4,4,4,4,4,4},{4,4,4,4,4,4,4,4}};
int* sum =0;
int* sumhost = 0;
sumhost = (int*)malloc(sizeof(int));
cout << *sumhost << endl;
float* devPtr;
size_t pitch;
cudaMallocPitch((void**)&devPtr, &pitch, width * sizeof(int), height);
cudaMemcpy2D(devPtr,pitch,testarray,0,8* sizeof(int),4,cudaMemcpyHostToDevice);
cudaMalloc((void**)&sum, sizeof(int));
kernel<<<1, 4>>>(devPtr, pitch, sum);
cutilCheckMsg("kernel launch failure");
cudaMemcpy(sumhost, sum, sizeof(int), cudaMemcpyDeviceToHost);
cout << *sumhost << endl;
return 0;
}

这段代码编译得很好(在 4.0 sdk 候选版本上)。但是,一旦我尝试执行,我就会得到

0
cpexample.cu(43) : cutilCheckMsg() CUTIL CUDA error : kernel launch failure : invalid pitch argument.

这很不幸,因为我不知道如何解决它;-(。据我所知,音高是内存中的偏移量,以便更快地复制数据。但是,这样的音高仅用于设备内存,而不用于主机内存,不是吗?因此,我的主机内存的间距应该是0,不是吗?

此外,我还想问另外两个问题:

  • 如果我声明一个像 int* sumhost 这样的变量(见上文),这个指针指向哪里?首先到主机内存,然后到cudaMalloc到设备内存?
  • 在这种情况下,cutilCheckMsg非常方便。我应该知道有类似的调试功能吗?

在代码的这一行中:

cudaMemcpy2D(devPtr,pitch,testarray,0,8* sizeof(int),4,cudaMemcpyHostToDevice);

你是说testarray的源音高值等于0,但是当音高公式T* elem = (T*)((char*)base_address + row * pitch) + column时,这怎么可能呢? 如果我们在该公式中用 0 的值代替音高,那么在查找某个二维 (x,y) 有序对偏移量的地址时,我们将无法获得正确的值。要考虑的一件事是音高值的规则是 pitch = width + padding . 在主机上,填充通常等于 0 ,但宽度不0,除非数组中没有任何内容。 在硬件端可能有额外的填充,这就是为什么间距的值可能不等于数组的声明宽度。 因此,您可以根据填充值得出结论,pitch >= width。 因此,即使在主机端,源音高的值也应至少为每行的大小(以字节为单位),这意味着在testarray的情况下,它应该是8*sizeof(int)。 最后,主机中 2D 数组的高度也只有 2 行,而不是 4 .

作为有关分配的指针会发生什么情况的问题的答案,如果使用 malloc() 分配指针,则会为指针提供一个驻留在主机内存中的地址值。 因此,您可以在主机端取消引用它,但不能在设备端取消引用。 另一方面,分配了cudaMalloc()的指针被赋予指向驻留在设备上的内存的指针。 因此,如果在主机上取消引用它,它不会指向主机上分配的内存,并且会出现不可预知的结果。 不过,将此指针地址传递给设备上的内核是可以的,因为当它在设备端取消引用时,它指向设备本地可访问的内存。 总体而言,CUDA 运行时将这两个内存位置分开,提供内存复制函数,这些功能将在设备和主机之间来回复制,并根据所需的方向(主机到设备或设备到主机)使用这些指针中的地址值作为复制的源和/或目标。 现在,如果您采用相同的int*,并首先使用malloc()分配它,然后(在希望调用指针free()之后)使用cudaMalloc(),则指针将首先具有指向主机内存的地址,然后是设备内存。 您必须跟踪其状态,以避免取消引用设备或主机上的地址而导致不可预知的结果,具体取决于该地址是在主机代码中还是在设备代码中取消引用的。