OpenCL内核似乎没有获得全局id“全局”

时间:2013-12-08 10:55:25

标签: c parallel-processing opencl

我正在尝试将我所做的程序转换为OpenCL,但我还不熟悉它。不过,我遇到了我的(三个)内核之一的问题。它基本上是一个复杂的矩阵向量乘法,但我写的是为了更好地满足我的需要。

问题是,我无法让内核在GPU上工作。我已将它简化为最多(2行),在CPU上调试,并且它在CPU上运行完美。但是当谈到GPU时,一切都搞砸了。我正在研发MacBook Pro,在NVIDIA GeForce 650M上我得到了一个结果,而在集成的Intel HD 4000上,我得到了另一个结果。内核是

__kernel void Chmv_(__global float2 *H, const float alpha, __global float2 *vec, 
                const int off/*in number of elements*/,
                __local float2 *vw,
                __global float2 *vout) 
{
int gidx=get_global_id(0);
int gidy=get_global_id(1);
int gs=get_global_size(0);

    vout[gidx].x += alpha*(H[gidx+gidy*gs].x*vec[gidy].x-H[gidx+gidy*gs].y*vec[gidy].y);
    vout[gidx].y += alpha*(H[gidx+gidy*gs].y*vec[gidy].x+H[gidx+gidy*gs].x*vec[gidy].y);

}

对于测试,我让矩阵H是一个4x4矩阵,填充(1.0f,0.0f),而输入矢量vec是x分量(0.0,1.0,2.0,3.0) )和y组件0. alpha设置为2.0f。所以,我应该(12,12,12,12)作为x输出,如果我使用CPU,我会这样做。 NVIDIA给了我6.0,而英特尔给了我4.0。

现在,仔细检查显示,如果输入向量是(0,1,2,0),NVIDIA给出0作为答案,如果是(0,1,0,3),则Intel给出0作为好。顺便说一下,为vec[gidy]更改vec[gidx]会让我的向量加倍。从这些方面来看,在我看来,内核只在一维x中执行良好,而get_global_id(1)只有一个值,这显然不行。

我将添加调用此内核检查的测试函数。现在,任何人都知道会发生什么事情?

void _test_(){
cl_mem mat,vec, out;
size_t gs[2]={4,4};
size_t ls[2]={1,4};
size_t cpuws[2]={1,1};
cl_float2 *A=(cl_float2*)calloc(gs[0]*gs[0], sizeof(cl_float2));
cl_float2 *v=(cl_float2*)calloc(gs[0], sizeof(cl_float2));
cl_float2 *w=(cl_float2*)calloc(gs[0], sizeof(cl_float2));
int i;

for (i=0; i<gs[0]; i++) {
    A[i*gs[0]].x=1.0;
    A[i*gs[0]+1].x= 1.0;//(i<ls-1)? 1.0f:0.0f;
    A[i*gs[0]+2].x=1.0;
    A[i*gs[0]+3].x=1.0;
    v[i].x=  (float)i;
    printf("%d %f %f %f %f\n%v2f\n",i, A[i*gs[0]].x, A[i*gs[0]+1].x, A[i*gs[0]+2].x, A[i*gs[0]+3].x, v[i]);
}
v[2].x=0.0f; //<--- set individually for debug

mat = clCreateBuffer(context, CL_MEM_READ_WRITE, gs[0]*gs[0]*sizeof(cl_float2), NULL, NULL);
vec = clCreateBuffer(context, CL_MEM_READ_WRITE, gs[0]*sizeof(cl_float2), NULL, NULL);
out = clCreateBuffer(context, CL_MEM_READ_WRITE, gs[0]*sizeof(cl_float2), NULL, NULL);

error = clEnqueueWriteBuffer(queue, mat, CL_TRUE, 0, gs[0]*gs[0]*sizeof(cl_float2), A, 0, NULL, NULL);
error = clEnqueueWriteBuffer(queue, vec, CL_TRUE, 0, gs[0]*sizeof(cl_float2), v, 0, NULL, NULL);
error = clEnqueueWriteBuffer(queue, out, CL_TRUE, 0, gs[0]*sizeof(cl_float2), w, 0, NULL, NULL);

int offset=0;
float alpha=2.0;
error  = clSetKernelArg(Chmv_, 0, sizeof(cl_mem),&mat);
error |= clSetKernelArg(Chmv_, 1, sizeof(float), &alpha);
error |= clSetKernelArg(Chmv_, 2, sizeof(cl_mem),&vec);
error |= clSetKernelArg(Chmv_, 3, sizeof(int), &offset);
error |= clSetKernelArg(Chmv_, 4, gs[0]*sizeof(cl_float2), NULL);
error |= clSetKernelArg(Chmv_, 5, sizeof(cl_mem), &out);
assert(error == CL_SUCCESS);

error = clEnqueueNDRangeKernel(queue, Chmv_, 2, NULL, gs, NULL, 0, NULL, &event);

error = clEnqueueReadBuffer(queue, out, CL_TRUE, 0, gs[0]*sizeof(cl_float2), w, 0, NULL, NULL);
clFinish(queue);

for (i=0; i<gs[0]; i++) {
    printf("%f %f\n", w[i].x, w[i].y);

}

clReleaseMemObject(mat);
clReleaseMemObject(vec);
clReleaseMemObject(out);
}

1 个答案:

答案 0 :(得分:1)

您遇到了对公共内存区域进行多线程不安全访问的典型问题。 (vout

您必须认为所有工作项都会同时运行。这意味着,他们将以任何顺序读写内存。

在CPU中执行时,问题不会显示,因为执行是由HW连续完成的。 然而,在GPU中,一些工作项读取vout的内存,递增并写入它。但是其他人也会在之前的工作项写入新值之前读取vout的内存。

可能所有的工作项都是并行运行的,因为你的内核很小,这就是你只看到其中一个添加到最终结果的原因。

这是典型的并行减少问题。您可以谷歌搜索更多详细信息。您需要实现的是在访问vout时同步所有线程,通过atomic_add()(慢)或适当减少(难以编码)。您可以查看本指南,它适用于CUDA,但或多或​​少基本相同:Reduction Guide