入门程序的错误

rt,尝试写了一个简单的入门程序。但是结果始终不对,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <cutil.h>
#include <cutil_inline.h>

struct PStruct
{
int PSize;
float *PMatrix;
};

global void KernelStruct(PStruct *pstruct)
{
int PSize=pstruct->PSize;
int Idx=threadIdx.x;
if (Idx<PSize)
{
pstruct->PMatrix[Idx]=pstruct->PMatrix[Idx]+1;
}
}

int main()
{
int i;
PStruct CPU_Struct,GPU_Struct;
//初始化CPU
int PSize=10;
CPU_Struct.PSize=PSize;
CPU_Struct.PMatrix=(float*)malloc(PSize*sizeof(float));
for (i=0;i<PSize;i++)
{
CPU_Struct.PMatrix[i]=i;
}

//初始化GPU
GPU_Struct.PSize=PSize;
cutilSafeCall(cudaMalloc((void**)&GPU_Struct.PMatrix,sizeof(float)*PSize));
cutilSafeCall(cudaMemcpy(GPU_Struct.PMatrix,CPU_Struct.PMatrix,sizeof(float)*PSize,cudaMemcpyHostToDevice));

//核函数
dim3 grim(PSize,1,1);
KernelStruct <<<1,grim>>>(&GPU_Struct);

//拷贝到CPU
cudaMemcpy(CPU_Struct.PMatrix,GPU_Struct.PMatrix,sizeof(float)*PSize,cudaMemcpyDeviceToHost);

//打印
FILE *pFile=fopen("Example.txt","w");
for (i=0;i<PSize;i++)
{
	fprintf(pFile,"PMatrix[%d]%f\n",i,CPU_Struct.PMatrix[i]);
}

//释放
cudaFree(GPU_Struct.PMatrix);
free(CPU_Struct.PMatrix);

return 0;

}

预期结果应该是1~10,但是运行结果却是0~9,也就是说核函数根本没有执行。请问这个是怎么回事?谢谢

哎,在核函数哪里不用指针就对了。。但是不知道是什么原因。。

看看这个帖子吧http://cudazone.nvidia.cn/forum/forum.php?mod=viewthread&tid=6820&extra=page%3D1

首先支持下3#,简单的说就是结构体里的指针并没有指向正确的内存位置。

仔细看了下你的代码,前面以为是链表的操作,回答有误,请参考其他版主的回复!

楼主您好。

您的kernel中使用的结构体中的指针PMatrix在device memory上,这个无问题的。
但是您的这结构体实例却在host memory上。

所以您不是“结构体里的指针”的问题。而是包含这个指针的结构体实例的问题。

您可以如下解决。
解决方案1:
(1)将贵kernel改成:
global void KernelStruct(PStruct pstruct)
{
int PSize=pstruct.PSize;
int Idx=threadIdx.x;
if (Idx<PSize)
{
pstruct.PMatrix[Idx]=pstruct.PMatrix[Idx]+1;
}
}
并将调用语句改成:
KernelStruct <<<1,grim>>>(GPU_Struct);
(注意他们分别将->改成了., 并将&GPU_Struct去掉了&号)

(2)或者您可以保持kernel不变,而将调用语句改成:
PStruct *ptr;
cudaMalloc(&prt, sizeof(struct PStruct));
cudaMemcpy(ptr, &GPU_Struct, sizeof(struct PStruct), cudaMemcpyHostToDevice);
这样无需改动kernel了。而将外层的结构体的实例也放到了device memory中。

再次强调一遍,“结构体里的指针的指向是正确的”,请以我的说法为准。

首先谢谢千军版主,还是有点不怎么明白。看了上面推荐的帖子,我认为你说的“所以您不是“结构体里的指针”的问题。而是包含这个指针的结构体实例的问题”是不是指我指针值的对象其实是在设备中的呢?这个和上面帖子在cudamemcpy中只复制地址还是有点不同。所以你说这是个“指针的结构体实例的问题”。如果是我想的这样,那么如果GPU_Struct已经在设备上了,用地址和直接给值不是一样的么?
还有第二种办法,可能我没有理解清楚,我这样改了一下

//初始化GPU
	GPU_Struct.PSize=PSize;
	PStruct *ptr;
	cudaMalloc(&ptr, sizeof(struct PStruct));
	cudaMemcpy(ptr, &GPU_Struct, sizeof(struct PStruct), cudaMemcpyHostToDevice);
	dim3 grim(PSize,1,1);
	KernelStruct <<<1,grim>>>(ptr);

	//拷贝到CPU
	cudaMemcpy(CPU_Struct.PMatrix,ptr->PMatrix,sizeof(float)*PSize,cudaMemcpyDeviceToHost);

但是在拷贝程序出错,提示溢出。。后面把前面的cudaMemcpyHostToDevice改成了cudaMemcpyDeviceToDevice,即:

GPU_Struct.PSize=PSize;
	PStruct *ptr;
	cudaMalloc(&ptr, sizeof(struct PStruct));
	cudaMemcpy(ptr, &GPU_Struct, sizeof(struct PStruct), cudaMemcpyDeviceToDevice);
	dim3 grim(PSize,1,1);
	KernelStruct <<<1,grim>>>(ptr);

	//拷贝到CPU
	cudaMemcpy(CPU_Struct.PMatrix,ptr->PMatrix,sizeof(float)*PSize,cudaMemcpyDeviceToHost);

结果能够正常运行,但是结果还是不对。不知道是怎么回事?谢谢

LZ您好,5#横扫斑竹给出的解释是正确的解释,请您参考。

以及我给您解释一下5#的说法,希望您能够明白。

首先,一个在device端使用的指针(无论是否在结构体里面),其指向的空间必然需要是指向device端的地址空间的。
其次,按照您的例子,您需要使用一个含有这种指针的结构体,您还需要明白这个结构体自身存储在神么地方,以及选择相应的使用方法。
您顶楼的例子中,先是定义了一个struct类型称为PStruct,然后在main()中定义了这个类型的两个实例:CPU_Struct和GPU_Struct。GPU_Struct这个实例本身是存储于host端的。之后,您将GPU_Struct这个结构体实例的地址作为参数,传给kernel。
请注意,GPU_Struct是存储于host端的,其地址也是host端的地址,因而您的kernel必然运行失败,因为kernel中使用的指针必须要指向device端的地址。
——这是横扫斑竹回答您的第一段的内容。(同时您应该能看明白“实例”表示的含义)

此时,应该如何解决此问题呢?
因为您的kernel只需要使用GPU_Struct这个实例的内容,所以您直接将kernel的参数类型改为PStruct类型,并将GPU_Struct作为调用kernel的参数即可。此时在调用kernel的时候,会自动复制GPU_Struct的值给kernel,并执行的。
这样改需要您相应修改kernel内部的相应访问shruct元素的写法。横扫斑竹给您指出了具体的修改方法。

——这是横扫斑竹的“解决方案1”。

如果您不愿意修改kernel的写法,那么您需要保证作为kernel的参数那个结构体指针必须指向device端内容。那么您可以1:在host端申请一个PStruct* 类型的指针ptr,2:在device端申请一个PStruct类型的长度的空间,并使用ptr指向该空间。(注意,ptr本身是保存在host端的,ptr指向device端的一端空间,这段空间保存了一个结构体。)3:将host端的GPU_Struct结构体的内容复制给device端ptr指向的空间。
这样,device端保存了GPU_Struct的一个副本,由ptr指针指向其地址。
kernel在使用ptr的时候,也可以直接取得device端保存的结构体,并正常使用。

——这是横扫斑竹给你建议的解决方案2。

此外,cudaMemcpy只要参数正确,一般不会随意出错的。
您可以详细检查和调试您的代码,如有问题,请给出报错信息和定位,以便分析问题。

祝您好运~

谁让你改第二个cudaMemcpy的??

你看到我的2个解决方案都怎么写了么?

你画蛇添足干嘛?

你直接抄成这样:
PStruct *ptr;
cudaMalloc(&ptr, sizeof(struct PStruct));
cudaMemcpy(ptr, &GPU_Struct, sizeof(struct PStruct), cudaMemcpyHostToDevice);

dim3 grim(PSize,1,1);
KernelStruct <<<1,grim>>>(ptr);

//下面的不要改!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

乱改了概不负责!!!!!!

LZ您好,以及我继续解释一下您的代码为何有问题(也就是横扫斑竹为何不让你改下面那部分):

您6#的第一段代码,第10行回拷部分的写法是不对的,其中第二个参数,您使用了ptr->PMatrix。
这种形式是通过指针访问结构体元素的方法,但是这里ptr指向的device端的地址(该地址上保存了GPU_Struct结构体的副本),该地址对于host端是无意义的,所以无法通过这个地址找到正确的参数。

所以您需要使用host端保存的副本GPU_Struct.PMatrix才行。

您6#的第二段代码,修改了第四行的cudaMemcpy的参数,改为cudaMemcpyDeviceToDevice,这个就有些莫名其妙了,您真的想清楚了这句代码干什么的么?参数之间是如何对应的么?

最后,友情提示一下,因为CUDA代码涉及host和device两个地址空间,经常需要仔细考虑指针指向何方,以及变量自身保存于何处,这样才能减少出错。

大致如上,祝您编码顺利~

不好意思,先没有看明白啊。。o(╯□╰)o

谢谢ice版主,打这么多字,而且讲得非常仔细啊。谢谢。经过你的解释,我想我是没有搞清楚host端和device端的关系。现在也不懂我们分配的空间到底是在GPU还是CPU上,比如:

GPU_Struct.PSize=PSize;
	cutilSafeCall(cudaMalloc((void**)&GPU_Struct.PMatrix,sizeof(float)*PSize));
	cutilSafeCall(cudaMemcpy(GPU_Struct.PMatrix,CPU_Struct.PMatrix,sizeof(float)*PSize,cudaMemcpyHostToDevice));

这个代码中,GPU_Struct分配空间是在GPU还是CPU上是由什么决定的呢?原来听说是由后面的参数cudaMemcpyHostToDevice决定的,但是感觉十分奇怪,因为如果我先只分配空间而不初始化的话,空间到底是在哪里呢?这样好像不能解释。
看了上面的代码感觉好像是在主机上生成的对象都是在CPU上的,但是主机上的指针可以指向GPU也可以指向CPU,但是到底是指向哪里是有后面的参数比如cudaMemcpyHostToDevice决定的,不知道这样理解对不对?
还有在

cudaMemcpy(ptr, &GPU_Struct, sizeof(struct PStruct), cudaMemcpyHostToDevice);

中,是把主机的值复制给设备,那么这里如果用

cudaMemcpy(ptr, &CPU_Struct, sizeof(struct PStruct), cudaMemcpyHostToDevice);

为什么不对呢?这里GPU_Struct和CPU_Struct是否都是在主机上啊?
而且,如果用第一种即GPU_Struct,那么在复制到GPU中储存的是指向CPU的指针么?这样所以才能在后面用GPU_Struct打印出来在GPU中经过操作后的值,但是这样变成对CPU中的值进行操作了,明显不对啊。但是为什么GPU_Struct.PMatrix复制到CPU_Struct.PMatrix就能够打印出来进过操作的值呢?
所以,总的问题就是如果按照第二种改法CPU_Struct、GPU_Struct和ptr在空间的存储关系是什么?到底是在CPU上还是GPU上。谢谢

LZ您好,您还是没有理解问题的关键所在。

其实问题的关键只有一点:某个变量存在哪里只和定义这个变量的方式有关,某段线性内存在什么地方,只和申请这段空间的方式有关。这些都和copy没有任何关系。

您只有明白这一点,才能捋清您的问题。

祝您好运~

恩,我也知道是这个问题啊。但是我就是不知道什么情况下申请的是在CPU上,什么时候申请的是在GPU上,版主能够以上面的GPU_Struct、CPU_Struct还有Pstruct的指针ptr做一个说明么?
其实我说和Copy有关,还是听一个比较有经验的人讲的。。或许还是有一些人对这个不是很清楚吧。。谢谢

楼主确定不是来灌水的么?

我已经无法继续服务您了。

请谅解。

横扫斑竹。。实在对不起。。可能问题太弱了。。我自己在查一下吧。。但是真心不是灌水的。。敬请谅解!

ice版主。。我想我知道问题在哪里了。。我一直以为GPU_Struct和pmatrix是一个东西呢。。所以一直纠结为什么GPU_Struct在CPU上,而pmatrix在GPU上这个矛盾的问题。。。哎。。还是基本概念不清楚啊。。不好意思。。也多谢ice版主的细心解答。。谢谢

LZ你好。仅针对你的例子:
1、GPU_struct是结构体指针,而pmatrix是结构体内部的一个成员,其数据类型是指针——版主们的回复都是建立在你了解这些C的基础只是的前提下,毕竟这CUDA的论坛,不是C的,请你仔细复习C中关于指针的概念。
2、对于一个在main函数里声明的指针(如int*p ,pstruct *p等),其存在位置是内存空间。而它所指向位置在哪里取决于——唯一取决于你分配方式。你用alloc/malloc/cudaHostAlloc/cudaMallocHost等方式分配空间,则其指向的位置是内存空间,你用cudaalloc方式分配空间,则其指向的位置是显存空间。这跟cudaMemcpy完全没有任何关系!!!!!!!!!!
3、以上仅是对两位版主所将内容的扩展,另外,其实你的问题在一开始就被两位版主指出了,请不要怀疑。

嗯,谢谢。。没有怀疑啊。。现在已经知道了。。多谢。。

LZ您好,17#的网友yixi再次解释了之前的内容。

就您16#的说法,我补充一点。

指针pmatrix作为结构体的成员,它自身的存储位置和它所在的结构体实例的存储位置是一致的。

同时pmatrix作为指针,可以保存一个地址。这个地址究竟是host端的地址还是device端的地址,取决于您分配空间的形式。以及您使用的时候需要明白这个地址是host的还是device的,host端的地址在device端是无意义的,反之亦然。

以及您在使用runtime API的时候,参数需要是host端可访问的(即参数本身是存储在host端的),但是部分指针参数所存储的地址需要是device端的地址,比如您使用cudaMemcpy的时候,如果是hostTodevice,那么目的地对应的地址就是device端的。此时您需要一个保存在host但指向device地址的指针。

大致如此了,祝您好运~

恩,这个问题已经明白了。谢谢。。。