CUDA - memcpy2d - 错误的音调

时间:2011-05-17 16:55:04

标签: c++ cuda

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

#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在这种情况下非常方便。是否有类似的调试功能我应该知道?

1 个答案:

答案 0 :(得分:4)

在你的代码的这一行:

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的值可能不等于所声明的阵列宽度的原因。因此,您可以根据填充值得出结论pitch >= width。因此,即使在主机端,源间距的值应至少为每行的大小(以字节为单位),在testarray的情况下,它应为8*sizeof(int)。最后,主机中2D阵列的高度也只有2行,而不是4

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