Python (Py)OpenCL同时修改来自所有线程的值
很简单,我有下面的内核,它修改了Python (Py)OpenCL同时修改来自所有线程的值,python,opencl,pyopencl,Python,Opencl,Pyopencl,很简单,我有下面的内核,它修改了C[0]的值,其中C是一个只有一个元素的数组 __kernel void sigma(__global float *A, __global float *B, __global float *C) { int i = get_global_id(0); printf("Adding %.2f + %.2f", A[i], B[i]); C[0] += A[i] + B[i]; } 问题是,最后C[0]有最后完成的线程
C[0]
的值,其中C
是一个只有一个元素的数组
__kernel void sigma(__global float *A, __global float *B, __global float *C) {
int i = get_global_id(0);
printf("Adding %.2f + %.2f", A[i], B[i]);
C[0] += A[i] + B[i];
}
问题是,最后C[0]
有最后完成的线程的值,特别是在本例中,我得到了以下结果
Adding 1.00 + 0.00
Adding 2.00 + 1.00
Adding 3.00 + 1.00
Adding 4.00 + 1.00
[5.]
最后,C[0]
是4.00+1.00
。我想要的是C[0]
成为(1.00+0.00)+(2.00+1.00)+(3.00+1.00)+(4.00+1.00)
。因此,我希望将每个线程的A[I]
和B[I]
添加到C[0]
此外,我不只是寻找补充,我希望这是与任何功能或操作兼容
这可能是多余的,但在主机代码中,我只是尽可能地将数据传递到内核。这个问题与主机代码有关吗
import pyopencl as cl, numpy as np;
ctx = cl.create_some_context(); queue = cl.CommandQueue(ctx); mf = cl.mem_flags
M = np.array([1, 2, 3, 4]).astype(np.float32) # A
V = np.array([0, 1, 1, 1]).astype(np.float32) # B
a = np.array([0]).astype(np.float32) # C
# Transfer data to GPU
A_GPU = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=M)
B_GPU = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=V)
C_GPU = cl.Buffer(ctx, mf.WRITE_ONLY, a.nbytes)
c = np.zeros(shape=a.shape, dtype= np.float32) # array to copy the result
kernel.sigma(queue, M.shape, None, A_GPU, B_GPU, C_GPU)
cl.enqueue_copy(queue, c, C_GPU).wait()
内核的编写方式效率低下,在许多情况下会导致不确定的结果。通常,应该避免多个线程(工作项)同时写入同一地址。在你的例子中,
C[0]+=A[i]+B[i]
是一个读-修改-写序列,从并行访问的角度来看,这会使事情变得更糟
您需要使用的是并行归约模式。
此模式允许您将数组中的所有元素高效且确定地求和为单个值(或者在您的情况下为-2数组)。它还支持除加法之外的许多其他关联运算符
在线资源很多,您可以根据自己的情况进行调整。正如@Elad Maimoni所说,在PyOpenCl中执行reduce的一种方法是使用
2.0版中提供的功能work\u group\u reduce\u add
在python中,它可以这样实现
- 内核
- 主机代码的其余部分
这将输出[90]
kernel = cl.Program(ctx, """
__kernel void resum(__global float *A, __global float *B, __global float *a) {
int i = get_global_id(0);
a[0] = work_group_reduce_add(A[i] + B[i]);
}
""").build(options='-cl-std=CL2.0') # Build using cl 2.0
# Some example arrays
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
b = np.array([9, 8, 7, 6, 5, 4, 3, 2, 1])
c = np.array([0])
d = np.array([0])
# Create GPU data
a = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=a)
b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=b)
c = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=c)
kernel.resum(queue, (10, ), None, a, b, c)
cl.enqueue_copy(queue, d, c)
print(d)