什么是OpenCL中本地和全局内存栅栏的区别?

时间:2017-09-22 21:02:01

标签: python-2.7 pyopencl

我试图用PyOpenCL做一个减少总和,类似于例子:https://dournac.org/info/gpu_sum_reduction。我试图用所有值1求和一个向量。第一个元素的结果应该是16384。 然而,似乎只收集了一些点。是否需要本地指数?是否存在任何竞争条件(当我运行两次时结果不一样)?以下代码有什么问题?

import numpy as np
import pyopencl as cl

def readKernel(kernelFile):
    with open(kernelFile, 'r') as f:
        data=f.read()
    return data

a_np = np.random.rand(128*128).astype(np.float32)
a_np=a_np.reshape((128,128))
print(a_np.shape)

device = cl.get_platforms()[0].get_devices(cl.device_type.GPU)[0]
print(device)
ctx=cl.Context(devices=[device])
#ctx = cl.create_some_context() #ask which context to use 
queue = cl.CommandQueue(ctx)
mf = cl.mem_flags

a_g = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=a_np)

prg = cl.Program(ctx,readKernel("kernel2.cl")).build()

prg.test(queue, a_np.shape, None, a_g)

cl.enqueue_copy(queue, a_np, a_g).wait()
np.savetxt("teste2.txt",a_np,fmt="%i")

内核是:

__kernel void test(__global float *count){
    int id = get_global_id(0)+get_global_id(1)*get_global_size(0);
    int nelements = get_global_size(0)*get_global_size(1);

    count[id] = 1;
    barrier(CLK_GLOBAL_MEM_FENCE); 

    for (int stride = nelements/2; stride>0; stride = stride/2){
        barrier(CLK_GLOBAL_MEM_FENCE); //wait everyone update
        if (id < stride){
            int s1 = count[id];
            int s2 = count[id+stride];
            count[id] = s1+s2;
        }
    }
    barrier(CLK_GLOBAL_MEM_FENCE); //wait everyone update
}

1 个答案:

答案 0 :(得分:0)

问题在于你的内核被实现为在一个工作组中进行缩减,并且有许多工作组被隐含地调整。

根据GPU的不同,每个工作组的最大工作项数量也不同。对于1024的Nvidia,AMD和Intel 256(旧GPU中的Intel有512)。

让我们假设您的GPU上每个工作组的最大工作项数为256.在这种情况下,最大2d worgroup大小可以是16x16,因此如果使用矩阵的大小,内核将返回正确的结果。在调度内核时使用原始大小128x128并且不指定本地大小,实现计算为您而且您将获得全局大小128x128和本地大小(很可能)16x16,这意味着正在调度8个worgroup。 在当前内核中,每个工作组从不同的id开始计算,但索引会减少到0,因此您有竞争条件,因此每次运行都会产生不同的结果。

您有2个选项可以解决此问题:

  1. 重写内核以计算一个工作组中的所有内容,并使用全局,本地大小安排它:(16x16),(16,16)或每个工作组设备的最大工作项数
  2. 使用全局,本地大小:(128x128),(16x16),每个工作组将计算其结果,然后在cpu方面必须为每个工作组求和以获得最终结果。
  3. 对于128x128,第一个选项将是首选,因为它应该更快,并且应该更直接实现。