如何使用 pyopencl 将字符串列表传递给 OpenCL 核?
我尝试使用缓冲区(见以下代码)的这种方式,但失败了。
OpenCL (struct.cl):
typedef struct{
uchar uc[40];
} my_struct9;
inline void try_this7_now(__global const uchar * IN_DATA,
const uint IN_len_DATA,
__global uchar * OUT_DATA){
for (unsigned int i = 0; i < IN_len_DATA; i++) OUT_DATA[i] = IN_DATA[i];
}
__kernel void try_this7(__global const my_struct9 * pS_IN_DATA,
const uint IN_len,
__global my_struct9 * pS_OUT){
uint idx = get_global_id(0);
for (unsigned int i = 0; i < idx; i++) try_this7_now(pS_IN_DATA[i].uc, IN_len, pS_OUT[i].uc);
}
Python (opencl_struct.py):
# -*- coding: utf-8 -*-
import pyopencl as cl
import pyopencl.array as cl_array
import numpy
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
# --------------------------------------------------------
LIMIT = 40
mf = cl.mem_flags
import ctypes,sys,struct
"""
typedef struct{
uchar uc[40];
} my_struct9;
"""
INlist = []
INlist.append("That is VERY cool!")
INlist.append("It is a list!")
INlist.append("A big one!")
#INlist.append("But it failes to output. :-(") # PLAY WITH THOSE
INlist.append("WTF is THAT?") # PLAY WITH THOSE
print "INlist : "+str(INlist)
print "largest string "+str( max( len(INlist[iL]) for iL in range(len(INlist)) ) )
strLIMIT = str(LIMIT)
s7 = struct.Struct((str(strLIMIT+'s')*len(INlist)))
IN_host_buffer = ctypes.create_string_buffer(s7.size)
s7.pack_into(IN_host_buffer, 0, *INlist)
IN_dev_buffer = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=IN_host_buffer)
OUT_host_buffer = ctypes.create_string_buffer(s7.size)
OUT_dev_buffer = cl.Buffer(ctx, mf.WRITE_ONLY, len(OUT_host_buffer))
print "> len(OUT_host_buffer) "+str(len(OUT_host_buffer))
# ========================================================================================
f = open("struct.cl", 'r')
fstr = "".join(f.readlines())
prg = cl.Program(ctx, fstr).build()
#cl.enqueue_copy(queue, IN_dev_buffer, IN_host_buffer, is_blocking=True) # copy data to device
cl.enqueue_write_buffer(queue, IN_dev_buffer, IN_host_buffer).wait()
prg.try_this7(queue, (1,), None, IN_dev_buffer, numpy.uint32(LIMIT), OUT_dev_buffer)
# ========================================================================================
cl.enqueue_copy(queue, OUT_host_buffer, OUT_dev_buffer).wait()
SSS = s7.unpack_from(OUT_host_buffer,0)
# unpack here OUT_host_buffer
print "(GPU) output : "+str(SSS)+" "
for s in range(len(SSS)):
print ">>> (GPU) output : "+str(SSS[s])
第一次运行程序时,使用 "but it failes to output"作为第四个列表元素。 然后,我通过增加和减少列表中的元素来进行尝试。最后,出现了以下问题:
程序的输出应该是(短版本)
(GPU) output : That is VERY cool!
(GPU) output : It is a list!
(GPU) output : A big one!
(GPU) output : WTF is THAT?
但实际上是:
python opencl_struct.py
INlist : ['That is VERY cool!', 'It is a
list!', 'A big one!', 'WTF is THAT?']
largest string 18
len(OUT_host_buffer) 160
(GPU) output : ('That is VERY cool!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
'It is a
list!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
'A big
one!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
'But it failes to output.
:-(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
(GPU) output : That is VERY cool!
(GPU) output : It is a list!
(GPU) output : A big one!
(GPU) output : But it failes to output. :-(
正如你所见,第四个列表元素不同。
所以,也许我的方法是错误的,或者在 pyopencl 或其他地方存在 bug。
我的显卡是 NVidia 9400 GPU。
解决方案
你的代码对我来说似乎很复杂。有些部分对我来说不是很清楚。例如,我不明白你为什么只创建一个工作项:
prg.try_this7(queue, (1,), None,...)
这迫使你在内核(而不是使用可用的并行性)中循环遍历你的字符串。无论如何,如果我理解正确,你想将一些字符串发送到 GPU,将它们复制到另一个缓冲区,在主机端取回它们并显示它们。
如果是这样的话,这里有一个只使用 numpy 和 pyopencl 的版本:
import numpy as np
import pyopencl as cl
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
#内核使用每个字符传输一个工作项
prog_str = """kernel void foo(global char *in, global char *out, int size){
int idx = get_global_id(0);
if (idx < size){
out[idx] = in[idx];
}
}"""
prog = cl.Program(ctx, prog_str).build()
#注意字符串数组的类型是 '|S40',第三个元素的长度是 40,形状是 3,字节数是 120 (3 * 40)
original_str = np.array(('this is an average string',
'and another one',
"let's push even more with a third string"))
mf = cl.mem_flags
in_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=original_str)
out_buf = cl.Buffer(ctx, mf.WRITE_ONLY, size=str_size)
copied_str = np.zeros_like(original_str)
#在这里,使用 str_size 数量的工作项启动内核,在本例中为 120,这意味着一些工作项不会处理任何有意义的字符
#(并非所有字符串的长度都为 40),但这并不重要
prog.foo(queue, (str_size,), None, in_buf, out_buf