利用GPU进行字符串匹配
作者:李 磊
来源:《硅谷》2009年第02
        [摘要]GPU通过SIMDSingle Instruction Multiple Data,单指令多数据)对图像数据进行并行处理。字符串的匹配在信息检索、计算机病毒码匹配和生物基因技术领域中都有应用。探讨利用GPU进行字符串的匹配。
        [关键词]GPU BF CUDA
        中图分类号:TP3文献标识码:A文章编号:167175972009012005501
       
        一、引言
       
        NVIDIA公司在1999年发布GeForce 256图形处理芯片时首先提出GPUGraphic Process
ing Unit,图形处理芯片)的概念。GPU32位的浮点渲染精度、向量处理特征和超长流水线结构特点,使它具有对密集性数据的计算能力,在通用计算机上提供一种并行平台。目前,GPU在分布式计算、生物制药、天气预报等非图形数据处理领域都有广泛应用。
        字符串匹配在信息检索、计算机病毒码匹配和生物基因技术领域中都有应用。字符串的匹配算法有很多,比如BF算法、KMP算法、RK算法。
        CUDACompute Unified Device Architecture,统一计算设备架构)是用于GPU计算的开发环境,它是一个全新的软硬件架构,可以将GPU视为一个并行数据计算的设备,对所进行的计算进行分配和管理。CUDAGPU编程语言基于标准的C语言,对大多数程序员来说还是很容易掌握的。
        本文就BF算法在GPU环境下利用CUDA软件开发工具的实现展开讨论。
       
        二、介绍一下CUDA编程的特点
       
        通过CUDA编程时,将GPU看作可以并行执行多个线程的计算设备(compute device)。它作为主CPU的协处理器来运作。应用程序首先被主CPU执行过程,而应用程序数据并行的、计算密集的部分给GPU执行,GPU最后把计算的结果返回给主CPU中的应用程序。即GPU执行的部分函数化。要达到这种效果,可以将这样一个函数编译到GPU设备的指令集合中,并将得到的程序(叫做内核,kernel)下载到GPU设备上。CPUGPU都保留自己的DRAM,分别称为主机内存(host memory)和设备内存(device memory)。用户可以通过优化的API调用将数据从一个DRAM复制到其他DRAM中,而优化的API调用使用了设备的高性能直接内存访问(DMA)引擎。
        下面是在主CPU下完成的BF朴素匹配算法,C语言描述如下:
        int BFString(char* query , char* subject,)
        { int i , j , k , num= -1
        int m=strlen(query) //模式串长度
        int n=strlen(subject) //目标串长度
        for(i=0 i
        { j=0 k=i //从第i个位置开始搜索字符串字符串长度17模式串长度subject,看是否
        存在子字符串跟模式串query一样
        while( j< m && subject[k]= = query[j]){k++ j++ }
        if(j= =m) num++//条件成立,则到模式串,记录个数加1
        }
        return num //返回个数
        }
        这个函数里面的形式参数、局部变量都是在内存中定义的,CPU可以直接访问。
        为了体现GPU的并行性,我们把字符串的每次比较交给不同的线程完成。比如thread_0i=0位置开始,thread_1i=1位置开始,thread_2i=2位置开始。。。。。。thread_ni
=n位置开始(假设用n+1个线程来执行);然后thread_0i=n+1位置开始,thread_1i=n+2位置开始。。。。这样循环直到所有的搜索都完成。目前GeForce 8800GT一个块(block)最多只能有512threads。假设一个kernel只由一个block组成。这里需要在CUDA的初始化文件,加入下面的#define语句,定义thread数目。
        #define THREAD_NUM 256
        接着把kernel程序改成如下形式:
        __global__ static void BFString(char * query , char * subject ,int number int len_que , int len_sub )
        { //query模式串,subject目标串
        extern __shared__ int shared[];//共享内存空间,保存匹配的个数
        const int tid = threadIdx.x //获得线程的id
        // const int bid = blockIdx.x;获得blockid,此时block只有一块,故省略
        int i , j , k shared[id]=0;
        for(i=tid i
        { j=0k=i
        while( j< len_que && subject[k]= = query[j]){k++j++}
        if(j= =len_que) shared[tid]++
        }
        __syncthreads() //同步函数,所有线程到这步等待同步
        if(tid = = 0) {
        for(i = 1 i < THREAD_NUM i++) shared[0] += shared[tid]
        number = shared[0]//利用线程0来计算匹配的子字符串的个数
        }
        }
        这个函数里面的形式参数和其它变量是在GPU的存储器里面。那么在main()程序中,就需要利用CUDAcudamalloc()分配GPU的存储器空间和cudamemcpy()复制主存的内容到显存。调用kernel函数后,还要将空间释放(用cudafree()函数)和将number的值复制回主存(用cudamemcpy()函数)。函数的具体操作请查看CUDA相应的文档。
        由于1block中的thread的个数是有限制的,要想有更多的thread来参与计算,就必须增加block的数目。注意,一个block里面的线程有一个shared memoryblock之间的shared memory不能相互访问。比如,我们这个时候在#define THREAD_NUM的位置加上:
        #define BLOCK_NUM 32
        kernel程序中需要修改的主要是在for循环:
        for(i = bid * THREAD_NUM + tid i < len_sublen_que
        i += BLOCK_NUM * THREAD_NUM)
        { j=0k=i
        while( j< len_que && subject[k]= = query[j]){k++j++}
        if(j= =len_que) shared[tid]++
        }
        这里要注意一点,就是复制结果回去的时候要考虑到BLOCK_NUMshared[0]
       
        参考文献:
        [1]张庆丹、戴正华、冯圣中、孙凝晖,基于GPU的串匹配算法研究,计算机应用[]200626(7):1735-1737

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。