Python PyCUDA 2D数组实现(或使用字符串)

Python PyCUDA 2D数组实现(或使用字符串),python,arrays,string,cuda,pycuda,Python,Arrays,String,Cuda,Pycuda,我正试图在CUDA中使用一系列弦剑 我试图通过创建一个字符串将其展平,但为了索引它,每次内核运行时我都必须检查其中的一些内容。如果有9000个单词的长度为6个字符,我必须在最坏的情况下检查每个内核调用的53994个字符。所以我在寻找不同的方法 更新:忘了提到,字符串的长度不同,所以我必须找到每个字符串的结尾 接下来,我尝试将每个字复制到不同的内存位置,然后收集地址,并将其作为一个数组传递给GPU,其中包含以下代码: # np = numpy wordList = ['asd','bsd','c

我正试图在CUDA中使用一系列弦剑

我试图通过创建一个字符串将其展平,但为了索引它,每次内核运行时我都必须检查其中的一些内容。如果有9000个单词的长度为6个字符,我必须在最坏的情况下检查每个内核调用的53994个字符。所以我在寻找不同的方法

更新:忘了提到,字符串的长度不同,所以我必须找到每个字符串的结尾

接下来,我尝试将每个字复制到不同的内存位置,然后收集地址,并将其作为一个数组传递给GPU,其中包含以下代码:

# np = numpy

wordList = ['asd','bsd','csd']

d_words = []

for word in wordList:
    d_words.append(gpuarray.to_gpu(np.array(word, dtype=str)))

d_wordList = gpuarray.to_gpu(np.array([word.ptr for word in d_words], dtype=np.int32))

ker_test(d_wordList, block=(1,1,1), grid=(1,1,1))
在内核中:

__global__ void test(char** d_wordList) {
    printf("First character of the first word is: %c \n", d_wordList[0][0]);
}
内核应该得到一个指向每个单词开头的int32指针数组,实际上是一个char**或int**,但它并不像我预期的那样工作

这种方法有什么问题

另外,在PyCUDA甚至CUDA中使用字符串的标准方法是什么


提前感谢。

经过进一步思考,我得出结论,在考虑内核内的数据访问问题时,对于变长字符串的情况,使用偏移量数组可能与二维索引(即双指针索引)没有太大区别。两者都有一定程度的间接性

下面是一个工作示例,演示了这两种方法:

$ cat t5.py
#!python
#!/usr/bin/env python
import time
import numpy as np
from pycuda import driver, compiler, gpuarray, tools
import math
from sys import getsizeof

import pycuda.autoinit

kernel_code1 = """
__global__ void test1(char** d_wordList) {
      (d_wordList[blockIdx.x][threadIdx.x])++;
}
    """

kernel_code2 = """
__global__ void test2(char* d_wordList, size_t *offsets) {
    (d_wordList[offsets[blockIdx.x] + threadIdx.x])++;
}
    """




mod = compiler.SourceModule(kernel_code1)
ker_test1 = mod.get_function("test1")



wordList = ['asd','bsd','csd']

d_words = []

for word in wordList:
    d_words.append(gpuarray.to_gpu(np.array(word, dtype=str)))

d_wordList = gpuarray.to_gpu(np.array([word.ptr for word in d_words], dtype=np.uintp))

ker_test1(d_wordList, block=(3,1,1), grid=(3,1,1))

for word in d_words:
  result = word.get()
  print result

mod2 = compiler.SourceModule(kernel_code2)
ker_test2 = mod2.get_function("test2")
wordlist2 = np.array(['asdbsdcsd'], dtype=str)
d_words2 = gpuarray.to_gpu(np.array(['asdbsdcsd'], dtype=str))
offsets = gpuarray.to_gpu(np.array([0,3,6,9], dtype=np.uint64))
ker_test2(d_words2, offsets, block=(3,1,1), grid=(3,1,1))
h_words2 = d_words2.get()
print h_words2


$ python t5.py
bte
cte
dte
['btectedte']
$
注:

对于双指针的情况,与OP的示例相比,唯一的变化是使用numpy.uintp类型作为指针,正如@talonmies在注释中所建议的那样

我不认为数据的双指针访问必然比与偏移量查找方法相关联的间接访问更快或更慢。另一个性能考虑因素是将数据从主机复制到设备,反之亦然。我相信,双指针方法有效地涉及多个分配和多个复制操作,这两个方向都是如此。对于许多字符串,这在主机/设备数据复制操作中很明显

偏移量方法的另一个可能优点是很容易确定每个字符串的长度——只需减去偏移量列表中的两个相邻条目。这可能很有用,以便轻松确定有多少线程可以并行操作一个字符串,而不是让单个线程顺序操作一个字符串,或使用内核代码中的方法确定字符串长度,或传递每个字符串的长度


现在还不清楚为什么压扁字符串会导致算法上的困难。我称之为标准方法。也不清楚为什么您认为指针数组适合int32数组。您正在使用32位操作系统吗?忘了提到字符串的长度不一样,所以我必须检查每个字符是否是字符串的结尾。无论是编写还是计算,这看起来都很乏味。因此,第二种方法会更容易使用,但我不知道它会带来什么性能差异。啊,int32可能是个问题,出于某种原因,我认为它已经足够了。除了传递字符串的压缩数组,还要传递起始偏移量的压缩数组,而不是每个字符串的指针。当您打包字符串时,应该很容易组装它,并且应该很容易作为int32数组传递,并且使用它在CUDA内核本身中进行基于指针的访问,而不是使用您在这里讨论的双指针方法,应该更容易、更有效。每个字符串的长度只是一个索引/偏移量和下一个之间的差值。谢谢!指针大小确实是个问题,我不知道为什么我认为内存地址是32位。另外,通过数组偏移的想法看起来相当方便,也是我应该考虑的问题。现在我有两个选择,所有的问题都解决了。我不想删除这个问题,以防有人遇到类似的问题,因此如果您发布了答案,那么我可以正确地关闭它。为了正确起见,您应该使用numpy.uintp作为指针的数据类型。PyCUDA内部就是这样做的,再次感谢详细的答案。经过一些测试,我发现偏移数组方法确实是可行的。由于内存操作,char**方法在处理大数据时变得异常缓慢。至少是我设法实现它的方式