C++ OpenCL:CPU而非GPU上的正确结果:如何正确管理内存?

C++ OpenCL:CPU而非GPU上的正确结果:如何正确管理内存?,c++,opencl,gpgpu,nvidia,C++,Opencl,Gpgpu,Nvidia,内核:好的,它编译成功了,我使用CPU作为设备获得了正确的结果,但那是在每次调用内核时,我都会释放程序并重新创建内存对象,这对于我的测试来说大约是16000次 我发布的代码就是我现在所在的位置,试图使用固定内存和映射 __kernel void CKmix(__global short* MCL, __global short* MPCL,__global short *C, int S, int B) { unsigned int i=get_global_id(0)

内核:好的,它编译成功了,我使用CPU作为设备获得了正确的结果,但那是在每次调用内核时,我都会释放程序并重新创建内存对象,这对于我的测试来说大约是16000次

我发布的代码就是我现在所在的位置,试图使用固定内存和映射

__kernel void CKmix(__global short* MCL, __global short* MPCL,__global short *C,  int S,  int B)
{       
    unsigned int i=get_global_id(0);
    unsigned int ii=get_global_id(1);
    MCL[i]+=MPCL[B*ii+i+C[ii]+S];
}
这也是成功的。我之所以有一个2d上下文数组,是因为我遍历了所有平台和设备,并允许用户选择要使用的平台和设备

OpenCLProgram = clCreateProgramWithSource(hContext[Plat-1][Dev-1],11, OpenCLSource, NULL ,NULL);
clBuildProgram(OpenCLProgram, 0,NULL,NULL, NULL,NULL);
ocKernel = clCreateKernel(OpenCLProgram, "CKmix", NULL);
我已经检查了错误,没有发现任何错误。内核使用新数据多次重复启动。我不知道我哪里做错了

NVIDIA 550 ti计算能力2.1, 最新的开发驱动程序,
Cuda SDK 4.0,

我不知道这是否是代码的唯一问题,但是:

clEnqueueWriteBuffer(hCmdQueue[Plat-1][Dev-1], DevMCL, CL_TRUE, 0, Z*NF*sizeof(short), MCL, 0, NULL, NULL);
clEnqueueWriteBuffer(hCmdQueue[Plat-1][Dev-1], DevCCL, CL_TRUE, 0, NF*sizeof(short), CCL, 0, NULL, NULL);
clEnqueueWriteBuffer(hCmdQueue[Plat-1][Dev-1], DevMO, CL_TRUE, 0, Z*sizeof(short), MTEMP, 0, NULL, NULL);

clEnqueueNDRangeKernel(hCmdQueue[Plat-1][Dev-1], ocKernel, 2, NULL, WorkSize, NULL, 0, NULL, NULL);
clEnqueueReadBuffer(hCmdQueue[Plat-1][Dev-1],DevMO, CL_TRUE, 0, Z * sizeof(short),(void*) MO , 0, NULL, NULL);
这绝对不是个好主意。通常会有多个线程在同一个
global\u id(0)
上工作,因此多个线程可能会尝试同时更新
MCL[i]
(请注意,
+=
不是原子的)。我认为,对于CPU来说,在大多数情况下,没有足够的线程来显示这种行为,而gpu上有数千个线程几乎肯定会导致问题

最合理的方法是只有一个一维工作集,并且每个螺纹累积到一个位置的所有值:

unsigned int i=get_global_id(0);
unsigned int ii=get_global_id(1);
MCL[i]+=MPCL[B*ii+i+C[ii]+S];
unsigned int i=get\u global\u id(0);
短累计=MCL[i]//或者0,如果这是开始的话
用于(int ii=0;ii

当然,这可能可行,也可能不可行。如果不是这样,修复可能不会那么简单。

那么到底出了什么问题?@Grizzly我没有得到正确的结果(MO)。当我使用GPU和CPU时,我也会得到不同的结果。那么,你得到了什么结果?你期望得到什么?@Grizzly内核在嵌套循环中启动,外部循环迭代7次,内部循环大约15000次。innerloop派生特定数据集的结果,而outerloop则遍历数据集。所以我得到了7个结果,它们是MO中元素的加法。在没有openCL的情况下得到的正确结果是:391725892,616085276,635390637,682414482,700946018,749609786,772387246。使用openCL的GPU上的结果是:220257766、401009434、551268540、678976664、945593751、1241266605、1504909805。但是GPU的结果会有几千种不同。当我在CPU上运行时,我得到的第一组数据的结果是正确的,而接下来的6组数据的结果是不正确的。对于一维工作集来说,
global\u id(0)
是唯一的吗,但不适用于二维或三维工作集?@SteveBlackwell:对于一维工作集,您的ID为0..N,其中每个线程获得其中一个。为一个2暗。您有id[0..N,0..M],其中每个组合发生一次,因此您得到M个线程共享相同的
global\u id(0)
(但具有不同的
global\u id(1)
)@Grizzly:您的解决方案有效。我现在在GPU上得到正确的结果!我仍然需要弄清楚如何优化它,因为它在CPU上的速度仅比串行方式快20%,比使用openMP的CPU慢3倍。我将尝试合并共享内存的使用,并致力于理解合并和对齐。谢谢@MVTCPLUSLUS:考虑到您最有可能的不是最佳的合并访问模式,这是意料之中的。也许我的这个答案有助于你理解合并:将工作大小设置为32的倍数会产生很大的不同<代码>while(工作大小%32!=0)--工作大小
++WorkSize
,使该过程快了大约三倍。
unsigned int i=get_global_id(0);
unsigned int ii=get_global_id(1);
MCL[i]+=MPCL[B*ii+i+C[ii]+S];
unsigned int i=get_global_id(0);
short accum = MCL[i]; //or 0, if thats the start
for(int ii = 0; ii < size; ++ii)
  accum += MPCL[B*ii+i+C[ii]+S];
MCL[i] = accum;