假设我有一个内核来计算两个数组的元素和。我不是将a,b和c作为三个参数传递,而是按如下方式构造它们:
typedef struct
{
__global uint *a;
__global uint *b;
__global uint *c;
} SumParameters;
__kernel void compute_sum(__global SumParameters *params)
{
uint id = get_global_id(0);
params->c[id] = params->a[id] + params->b[id];
return;
}
如果您使用PyOpenCL [1]的RTFM,还有其他人已经解决了这个问题[2] [3] [4]。但是我找不到的OpenCL结构示例都没有指针作为成员。
具体来说,我担心主机/设备地址空间是否匹配,以及主机/设备指针大小是否匹配。有谁知道答案?
[1] http://documen.tician.de/pyopencl/howto.html#how-to-use-struct-types-with-pyopencl
[2] Struct Alignment with PyOpenCL
[3] http://enja.org/2011/03/30/adventures-in-opencl-part-3-constant-memory-structs/
答案 0 :(得分:2)
不,没有保证地址空间匹配。对于基本类型(float,int,...),你有对齐要求(标准的第6.1.5节),你必须使用OpenCL实现的cl_type名称(当用C语言编程时,pyopencl完成工作我的工作) '说)。
对于指针,由于这种不匹配,它甚至更简单。标准v 1.2的6.9节(版本1.1的第6.8节)的开头是:
在作为指针的程序中声明的内核函数的参数 必须使用__global,__ constant或__local限定符声明。
在p。点:
声明为struct或的内核函数的参数 union不允许将OpenCL对象作为元素传递 结构或联合。
还请注意d。点:
可变长度数组和具有灵活(或未确定)的结构 不支持数组。
所以,没有办法让你按照你的问题中的描述进行内核运行,这就是为什么你无法找到一些OpenCl结构的例子有指针作为成员。
我仍然可以提出一个利用内核在JIT中编译的事实的解决方法。它仍然需要您正确打包数据并注意对齐,最后在程序执行期间大小不会改变。老实说,我会选择一个内核,将3个缓冲区作为参数,但无论如何,它都是。
我们的想法是使用预处理器选项-D,如以下python中的示例所示:
内核:
typedef struct {
uint a[SIZE];
uint b[SIZE];
uint c[SIZE];
} SumParameters;
kernel void foo(global SumParameters *params){
int idx = get_global_id(0);
params->c[idx] = params->a[idx] + params->b[idx];
}
主机代码:
import numpy as np
import pyopencl as cl
def bar():
mf = cl.mem_flags
ctx = cl.create_some_context()
queue = cl.CommandQueue(self.ctx)
prog_f = open('kernels.cl', 'r')
#a = (1, 2, 3), b = (4, 5, 6)
ary = np.array([(1, 2, 3), (4, 5, 6), (0, 0, 0)], dtype='uint32, uint32, uint32')
cl_ary = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=ary)
#Here should compute the size, but hardcoded for the example
size = 3
#The important part follows using -D option
prog = cl.Program(ctx, prog_f.read()).build(options="-D SIZE={0}".format(size))
prog.foo(queue, (size,), None, cl_ary)
result = np.zeros_like(ary)
cl.enqueue_copy(queue, result, cl_ary).wait()
print result
结果:
[(1L, 2L, 3L) (4L, 5L, 6L) (5L, 7L, 9L)]
答案 1 :(得分:0)
我不知道我自己的问题的答案,但有三种解决方法,我可以想到我的头脑。我认为解决方法3是最佳选择。
解决方法1:这里我们只有3个参数,所以我们可以只生成a,b和c内核参数。但我已经读过你可以传递给内核的参数数量有限制,我个人喜欢重构任何需要超过3-4个参数的函数来使用结构(或者,在Python中,元组或关键字参数) 。所以这个解决方案使得代码更难阅读,并且不能扩展。
解决方法2:将所有内容转储到单个巨型阵列中。然后内核看起来像这样:
typedef struct
{
uint ai;
uint bi;
uint ci;
} SumParameters;
__kernel void compute_sum(__global SumParameters *params, uint *data)
{
uint id = get_global_id(0);
data[params->ci + id] = data[params->ai + id] + data[params->bi + id];
return;
}
换句话说,不使用指针,而是将偏移量用于单个数组。这看起来非常像实现我自己的内存模型的开始,感觉它正在重新发明存在于PyOpenCL或OpenCL或两者中的某个轮子。
解决方法3:制作setter内核。像这样:
__kernel void set_a(__global SumParameters *params, __global uint *a)
{
params->a = a;
return;
}
并且同上set_b,set_c。然后使用worksize 1执行这些内核以设置数据结构。你仍然需要知道为params分配的块有多大,但如果它太大,就不会发生任何不好的事情(除了一点浪费的内存),所以我只想假设指针是64位。
这种解决方法的性能可能很糟糕(我想一个内核调用有很大的开销),但幸运的是,对我的应用程序来说应该不会太重要(我的内核一次会运行几秒钟,它不是图形的东西必须以30-60 fps运行,所以我想,额外内核调用设置参数所花费的时间最终只占我工作量的一小部分,无论每个内核调用开销有多高。) / p>