【问题标题】:How to pass a list of strings to an opencl kernel using pyopencl?如何使用 pyopencl 将字符串列表传递给 opencl 内核?
【发布时间】:2013-07-11 21:20:48
【问题描述】:

如何以正确的方式将字符串列表传递给 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] )

我第一次运行程序时将“但无法输出”作为第 4 个列表元素。然后我通过增加和减少列表的元素来玩弄。最后出现了这个问题: 程序的输出应该是(短版)

(GPU)输出:太酷了!

(GPU) 输出:这是一个列表!

(GPU)输出:很大!

(GPU) 输出:WTF 是这样吗?

但它是:

python opencl_struct.py

INlist : ['这太酷了!', '这是一个 列表!','一个大的!','WTF是那个?']

最大的字符串 18

len(OUT_host_buffer) 160 (GPU) 输出:('这非常酷!\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\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\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\x00')

(GPU)输出:太酷了!

(GPU) 输出:这是一个列表!

(GPU)输出:很大!

(GPU) 输出:但输出失败。 :-(

如您所见,第 4 个列表元素不同。

所以,也许我的方法是错误的,或者 pyopencl 或其他地方存在错误。

我使用的是 NVidia 9400 GPU。

兰博

【问题讨论】:

    标签: python opencl pyopencl


    【解决方案1】:

    在我看来,您的代码非常复杂。有些部分对我来说不是很清楚。例如,我不明白您为什么只创建一个工作项:

    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)
    #The kernel uses one workitem per char transfert
    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()
    #Note that the type of the array of strings is '|S40' for the length
    #of third element is 40, the shape is 3 and the nbytes is 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)
    #here launch the kernel with str_size number of workitems in this case 120
    #this mean that some of the workitems won't process any meaningful char 
    #(not all string have a lenght of 40) but it's no biggie
    prog.foo(queue, (str_size,), None, in_buf, out_buf, np.int32(str_size))
    cl.enqueue_copy(queue, copied_str, out_buf).wait()
    print copied_str
    

    以及显示的结果:

    ['this is an average string' 'and another one'
     "let's push even more with a third string"]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多