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,也就是说核函数根本没有执行。请问这个是怎么回事?谢谢
哎,在核函数哪里不用指针就对了。。但是不知道是什么原因。。
首先支持下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两个地址空间,经常需要仔细考虑指针指向何方,以及变量自身保存于何处,这样才能减少出错。
大致如上,祝您编码顺利~
谢谢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地址的指针。
大致如此了,祝您好运~