我刚开始进行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,不应该吗?
此外,我还想问另外两个问题:
答案 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()
,您的指针将首先具有指向主机内存的地址,然后是设备内存。您必须按顺序跟踪其状态,以避免因取消引用设备或主机上的地址而导致不可预测的结果,具体取决于它是否在主机代码或设备代码中取消引用。