使用pycuda替换字符串,使用cuda替换字符串

Posted 高颜值的殺生丸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用pycuda替换字符串,使用cuda替换字符串相关的知识,希望对你有一定的参考价值。

写一个python的cuda程序,实现字符串列表的字符串替换,把所有的123替换成xinyuuliu

 

以下是一个简单的 Python CUDA 代码示例,用于实现字符串列表的字符串替换。它使用了 NVIDIA CUDA 编程模型的一些基本概念和语法。

# 导入必要的模块
import pycuda.autoinit
import pycuda.driver as drv
import numpy as np
from pycuda.compiler import SourceModule

# CUDA 核心函数,用于实现字符串替换
mod = SourceModule("""
    #include <stdio.h>
    #include <string.h>
    #define N 100

    __global__ void replace(char **str, int n) 
        int idx = threadIdx.x + blockIdx.x * blockDim.x;
        int len = strlen(str[idx]);
        char temp[N] = 0;
        int j = 0;

        for (int i = 0; i < len; i++) 
            if (str[idx][i] == \'1\' && str[idx][i+1] == \'2\' && str[idx][i+2] == \'3\') 
                temp[j++] = \'x\';
                temp[j++] = \'i\';
                temp[j++] = \'n\';
                temp[j++] = \'y\';
                temp[j++] = \'u\';
                temp[j++] = \'u\';
                temp[j++] = \'l\';
                temp[j++] = \'i\';
                temp[j++] = \'u\';
                i += 2;
             else 
                temp[j++] = str[idx][i];
            
        
        temp[j] = \'\\\\0\';
        strcpy(str[idx], temp);
    
""")

# 准备字符串列表数据
str_list = [\'123hello123\', \'world123\', \'abc123def123\']
n = len(str_list)
str_array = np.array(str_list, dtype=np.string_)
str_dev = drv.mem_alloc(str_array.nbytes)
drv.memcpy_htod(str_dev, str_array)

# 运行 CUDA 核心函数
func = mod.get_function("replace")
func(str_dev, np.int32(n), block=(1, 1, 1), grid=(n, 1))

# 从 GPU 内存中获取替换后的字符串列表数据
result = np.empty_like(str_array)
drv.memcpy_dtoh(result, str_dev)

# 打印结果
print(result)

  这个程序会把字符串列表中所有的 123 替换成 xinyuuliu,并在终端输出替换后的字符串列表。注意,在代码中使用了 CUDA 的并行计算能力,因此程序的运行速度可能比较快。同时,由于 CUDA 编程需要硬件和软件的支持,你需要在运行前确保你的计算机具有 NVIDIA 的 CUDA-enabled GPU 和相应的软件环境。

 

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

我正在尝试使用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 
", d_wordList[0][0]);
}

内核应该得到一个int32指针数组,指向每个单词的开头,实际上是一个char **(或int **),但它不能像我期望的那样工作。

这种方法有什么问题?

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

提前致谢。

答案

经过一番深思,我得出结论,对于这种可变长度字符串的情况,使用“偏移数组”可能与2D索引(即双指针索引)没有太大的不同,在考虑数据访问的问题时核心。两者都涉及一定程度的间接。

这是一个展示两种方法的实例:

$ 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']
$

笔记:

  1. 对于双指针的情况,OP的例子中唯一的变化是使用numpy.uintp类型作为指针,如@talonmies的评论中所建议的那样
  2. 我不认为数据的双指针访问必然比与偏移查找方法相关的间接更快或更慢。另一个性能考虑因素是将数据从主机复制到设备,反之亦然。我认为,双指针方法有效地涉及两个方向的多个分配和多个复制操作。对于许多字符串,这在主机/设备数据复制操作中是显而易见的。
  3. 偏移方法的另一个可能的优点是很容易确定每个字符串的长度 - 只需减去偏移列表中的两个相邻条目。这可能很有用,这样可以很容易地确定有多少线程可以并行操作字符串,而不是让单个线程顺序处理字符串(或者使用内核代码中的方法来确定字符串长度,或者通过每个字符串的长度)。

以上是关于使用pycuda替换字符串,使用cuda替换字符串的主要内容,如果未能解决你的问题,请参考以下文章

从 Python 访问 OpenCV CUDA 函数(无 PyCUDA)

PyCUDA 和 NumbaPro CUDA Python 有啥区别?

为啥在这个例子中 PyCUDA 比 C CUDA 快

jetson nano安装pycuda

使用PyCUDA和固定内存的dot产品

GPU加速系列PyCUDA:上手简单操作