Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用PyOpenCL将带指针成员的结构传递给OpenCL内核_Python_Struct_Opencl_Memory Alignment_Pyopencl - Fatal编程技术网

Python 使用PyOpenCL将带指针成员的结构传递给OpenCL内核

Python 使用PyOpenCL将带指针成员的结构传递给OpenCL内核,python,struct,opencl,memory-alignment,pyopencl,Python,Struct,Opencl,Memory Alignment,Pyopencl,假设我有一个内核来计算两个数组的元素和。我没有将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);

假设我有一个内核来计算两个数组的元素和。我没有将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]

[2]

[3]


[4] 我不知道自己的问题的答案,但我可以想出3个解决办法。我认为工作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;
}
集合b和集合c也是如此。然后使用worksize 1执行这些内核以设置数据结构。您仍然需要知道为params分配的块有多大,但是如果它太大,则不会发生什么不好的事情(除了一点内存浪费),因此我认为只需假设指针是64位的


这种变通方法的性能可能很糟糕(我认为内核调用会带来巨大的开销),但幸运的是,这对我的应用程序来说并不重要(我的内核将一次运行几秒钟,它不是一个必须以30-60 fps的速度运行的图形,因此我想象额外内核调用设置参数所花费的时间将只占我工作负载的一小部分,不管每个内核调用的开销有多高)不,不能保证地址空间匹配。对于基本类型(float,int,…)您有对齐要求(标准第6.1.5节),并且必须使用OpenCL实现的cl类型名称(在C中编程时,我想pyopencl在后台完成任务)

对于指针,由于这种不匹配,它更简单。标准V1.2的第6.9节(版本1.1的第6.8节)一开始就说明:

在作为指针的程序中声明的内核函数的参数 必须使用_全局、_常量或_局部限定符声明

在p点:

声明为结构或函数的内核函数的参数 union不允许将OpenCL对象作为 结构或联合

另请注意d点:

可变长度数组和结构,具有灵活(或无大小) 不支持数组

因此,没有办法让您的内核按照您的问题中所述运行,这就是为什么您无法找到一些OpenCl结构的示例,其中包含指针作为成员。
我仍然可以提出一个解决方案,利用内核是在JIT中编译的这一事实。它仍然要求您正确地打包数据,注意对齐,最后在程序执行期间大小不会改变。老实说,我支持使用3个缓冲区作为参数的内核,但无论如何w、 就在那里

其思想是使用预处理器选项–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)]

我意识到这是6年后的事了,但是,解决方案3是有效的OpenCL吗?具体来说,访问作为内核参数传递的缓冲区之外的全局内存地址有效吗?我已经使用这种方法将一些CUDA代码移植到OpenCL 1.2,它在大多数GPU(英特尔、NVIDIA和ARM)上都有效但是,在一些AMD GPU上,对通过指针访问的缓冲区进行的更新似乎不会“粘住”(除非在内核中添加一个printf以获得额外的Sketchynes)。理论是:1)指针不是这些设备上的实际全局内存地址-不知道printf为什么会有帮助2)对缓冲区的更新正在得到优化