OpenCL中使用本地内存的并行约简

OpenCL中使用本地内存的并行约简,opencl,Opencl,我在OpenCL中实现了一个reduce内核来汇总大小为N的输入向量中的所有条目。为了更简单的测试,我用1.0f初始化输入向量。所以结果应该是N,但不是 这是我的reduce内核: 以下是OpenCL的设置: const uint N = 8196; cl_float a[N]; cl_float b[N]; for (uint i=0; i<N; i++) { a[i] = 1.0f; b[i] = 0.0f; } cl::Buffer inpu

我在OpenCL中实现了一个reduce内核来汇总大小为N的输入向量中的所有条目。为了更简单的测试,我用1.0f初始化输入向量。所以结果应该是N,但不是

这是我的reduce内核:

以下是OpenCL的设置:

 const uint N = 8196;

 cl_float a[N];
 cl_float b[N];

 for (uint i=0; i<N; i++) {
      a[i] = 1.0f;
      b[i] = 0.0f;
 }

 cl::Buffer inputBuffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_float)*N);
 cl::Buffer resultBuffer(context, CL_MEM_READ_ONLY, sizeof(cl_float)*N);

 queue.enqueueWriteBuffer(inputBuffer, CL_TRUE, 0, sizeof(cl_float)*N, a);
 queue.enqueueWriteBuffer(resultBuffer, CL_TRUE, 0, sizeof(cl_float)*N, b);

 cl::Kernel addVectorKernel = cl::Kernel(program, "reduce");

 size_t localSize = addVectorKernel.getWorkGroupInfo<CL_KERNEL_WORK_GROUP_SIZE>(device); // e.g. => 512

 size_t globalSize = roundUp(localSize, N); // rounds up to a multiple of localSize

 addVectorKernel.setArg(0, inputBuffer);
 addVectorKernel.setArg(1, resultBuffer);
 addVectorKernel.setArg(2, N);
 addVectorKernel.setArg(3, (sizeof(cl_float) * localSize), NULL);


 queue.enqueueNDRangeKernel(
      addVectorKernel,
      cl::NullRange,    
      cl::NDRange(globalSize), 
      cl::NDRange(localSize)     
 );
 queue.finish(); // wait for ending

 queue.enqueueReadBuffer(resultBuffer, CL_TRUE, 0, sizeof(cl_float)*N, b); // e.g. => 1024

结果取决于工作组的大小。我做错了什么?它是内核本身还是OpenCL的设置

在还原for循环中,您需要:

for(unsigned int s = localSize >> 1; s > 0; s >>= 1)
在初始化s时,移动的位比应该的多一位

修复后,让我们看看内核在做什么。主机代码以globalSize为8192和localSize为512执行它,这将导致16个工作组。在内核内部,首先将索引2*global_id处两个连续内存位置的数据相加。对于id为15的工作组,工作项0将位于索引15*512*2=15360和15361处,这两个位置位于输入数组的边界之外。我很惊讶你没有撞车。同时,这也解释了为什么您的值是预期值的两倍

要修复它,可以执行以下操作:

cache[localID] = input[globalID];

或者指定一个全局大小,该大小为当前大小的一半。

在reduce for循环中,您需要:

for(unsigned int s = localSize >> 1; s > 0; s >>= 1)
在初始化s时,移动的位比应该的多一位

修复后,让我们看看内核在做什么。主机代码以globalSize为8192和localSize为512执行它,这将导致16个工作组。在内核内部,首先将索引2*global_id处两个连续内存位置的数据相加。对于id为15的工作组,工作项0将位于索引15*512*2=15360和15361处,这两个位置位于输入数组的边界之外。我很惊讶你没有撞车。同时,这也解释了为什么您的值是预期值的两倍

要修复它,可以执行以下操作:

cache[localID] = input[globalID];

或者指定一个全局大小,该大小为当前大小的一半。

将总和写入全局内存时,应使用组的id

if (local_id == 0) output[local_size] = cache[0];
该行将重复写入输出[512]。您需要每个工作组写入输出中的专用位置

kernel void reduce(global float* input, global float* output, const unsigned int N, local float* cache)
{
    const uint local_id = get_local_id(0);
    const uint global_id = get_global_id(0);
    const uint group_id = get_group_id(0);
    const uint local_size = get_local_size(0);

    cache[local_id] = (global_id < N) ? input[global_id] : 0.0f;
    barrier(CLK_LOCAL_MEM_FENCE);

    for (unsigned int s = local_size >> 1; s > 0; s >>= 1) {
        if (local_id < s) {
            cache[local_id] += cache[local_id + s];
        }
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    if (local_id == 0) output[group_id] = cache[0];
}
然后需要对主机上的输出值求和。请注意,主机代码中的“b”不需要包含N个元素。每个工作组只使用一个元素

//replace (globalSize/localSize) with the pre-calculated/known number of work groups
for (i=1; i<(globalSize/localSize); i++) {
    b[0] += b[i];
}

现在b[0]是您的总计。

将总和写入全局内存时,您应该使用组的id

if (local_id == 0) output[local_size] = cache[0];
该行将重复写入输出[512]。您需要每个工作组写入输出中的专用位置

kernel void reduce(global float* input, global float* output, const unsigned int N, local float* cache)
{
    const uint local_id = get_local_id(0);
    const uint global_id = get_global_id(0);
    const uint group_id = get_group_id(0);
    const uint local_size = get_local_size(0);

    cache[local_id] = (global_id < N) ? input[global_id] : 0.0f;
    barrier(CLK_LOCAL_MEM_FENCE);

    for (unsigned int s = local_size >> 1; s > 0; s >>= 1) {
        if (local_id < s) {
            cache[local_id] += cache[local_id + s];
        }
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    if (local_id == 0) output[group_id] = cache[0];
}
然后需要对主机上的输出值求和。请注意,主机代码中的“b”不需要包含N个元素。每个工作组只使用一个元素

//replace (globalSize/localSize) with the pre-calculated/known number of work groups
for (i=1; i<(globalSize/localSize); i++) {
    b[0] += b[i];
}

现在b[0]是您的总计。

我想您是想使用arraySize=8192。这将是32kb的本地内存。谢谢你的评论,我更新了文本。我想你的意思是使用arraySize=8192。这将是32kb的本地内存。谢谢你的评论,我更新了文本。谢谢你的帖子,但这只是一个打字错误,结果本身没有问题-现在对于512的工作组大小只有1024。非常感谢,但很抱歉,它无论如何都不起作用。谢谢你的帖子,但这只是一个输入错误,结果本身并没有问题-现在对于512的工作组大小来说,它只是1024。非常感谢,但很抱歉,它无论如何都不起作用。谢谢。首先,我认为我们应该从最后提到的外观中的I=0开始。其次,问题仍然存在…i=1是正确的。我正在将所有其他值添加到b[0]。不过我有一个不同的错误:循环体应该是b[0]+=b[I];非常感谢。首先,我认为我们应该从最后提到的外观中的I=0开始。其次,问题仍然存在…i=1是正确的。我正在将所有其他值添加到b[0]。不过我有一个不同的错误:循环体应该是b[0]+=b[I];