H.264/AVC MOTION ESTIMATION IMPLMENTATION ON COMPUTE UNIFIED DEVICE ARCHITECTURE (CUDA)

来源:互联网 发布:ios开发必须用mac吗 编辑:程序博客网 时间:2024/05/19 00:41
H.264/AVC运动估计在CUDA上的实现

摘要

由于GPU的快速发展,使用GPU来辅助CPU计算大规模数据变得很重要。本文中,提出了一个高效的块级的并行算法,来解决分像素可变块大小的运动估计,基于2007年NVIDIA提出的CUDA平台。CUDA增强了GPU通用计算的可编程能力可灵活性。我们将H.264运动估计算法分解为5步,来实现低外部存储器传输速率下的高度并行计算。实验结果表明,在GPU的帮助下,处理速度是只用CPU的12。

关键字  H.264/AVC 运动估计 并行计算 CUDA GPU


1.引言

        H.264/AVC是一个高效压缩算法。为了实现高效率,它的算法复杂度高。因此,在普通CPU上实现有一定的难度。

过去几年内,GPU空前发展。随着多核和大存储带宽的实现,GPU的计算能力已经远高于CPU。由于它的高计算能力,GPU不仅用来加速图像显示,也用来加速非图形计算,例如线性代数计算和科学仿真。这种类型的应用因此叫做“GPU通用计算”(GPGPU)。

NVIDIA最近公布了一个强大的GPU架构,称作“CUDA”。它从根本上说是单程序多数据(SPMD)计算设备。然而,这个新结构结合了顶点着色器和像素着色器(这些最初针对不用的应用设计的)到一个通用计算设备,并且提供内部共享存储器来减少外部动态随机存储器(Dynamic Random Access Memory)。这些强化大幅提高了GPGPU的灵活性和可编程性。因此,我们在这个设备上实现H.264编码中最消耗时间的部分“运动估计”。

        虽然已经有一些基于GPU的运动估计算法,但是新的CUDA结构需要新的算法来充分利用它的特点。本文中,高并行变大小块全搜索分像素运动估计算法。我们的运动估计算法通过使用大量的并行线程针对CUDA结构优化,还能有效的使用共享存储器来减少DRAM的接入。实现细节在下面章节,基于NVIDIA GeForce 8800GTX GPU平台。

        文章下面部分组织如下。第二节大概介绍CUDA。第三节介绍算法和实现细节。第四节效果评估。第五节总结。

2. Compute Unified Device Architecture

2.1 硬件结构

        与之前GPU结构不同,CUDA结合了顶点着色器和像素着色器到一个统一的计算单元中,称作“流处理器,streaming processor(SP)”。例如,NVIDIA 8800GTX包括128个SP。每8个SP组成一个“流多处理器,streaming multiprocessors(SM)”。SM具有单指令多数据(SIMD)结构,对不同是数据执行相同的指令。

        四种类型的片上存储器和SM相关:(1)每个处理器一个32位general propose registers集合,(2)一个16组的共享存储器,被一个SM的所有SP共享,实现SP之间数据交流不使用其它外部芯片,(3)一个只读常量缓存,映射所有可访问的SP到DRAM设备的常量存储器,(4)一个只读的纹理缓存,映射所有可访问的SP到DRAM设备的纹理存储器。处理常量存储器和纹理存储器之外,还有针对SP的只读存储器,一个全局设备内置内存对于SP即可读又可写。然而,由于全局存储器缺少缓存,数据访问全局存储器要比访问常量存储器和纹理存储器慢的多。

 

2.2 编程和执行模型

        在CUDA编程中,源程序首先编译成CUDA设备指令集,它就变成了一个并行的新程序,称作“kernel”,kernel下载到GPU设备中,GPU作为一个CPU的协处理器。它按照线程原理执行,按照thread block组织在一起。thread block中的thread可以通过share memory协同工作,同步它们的执行来协调存储器访问。thread block 中的thread最大个数是受限制的;然而,执行同一个kernel的thread blocks 可以批处理形成一个block的grid。因此,执行单一kernel的thread的总数可以非常大。不过,不同thread blocks中的thread是不能访问同一个共享存储器的,它们独立的运行。

        SM可以同时执行一个或多个thread block,取决于shared memory和占有的寄存器。每个thread blocks被分成threads的SIMD组,称作“wrap”;这些wraps包含32个连续的thread,这些threads在SM中以SIMD的形式执行。如前所述,shared memory只有16个banks。因此,一个wrap需要的shared memory本分成两个部分。一个线程调度器定期的切换wrap,最大限度的利用SM的计算资源。

 

3. 运动估计onCUDA

3.1 H.264运动估计算法

        H.264/AVC中基本的运动补偿过程是16*16的宏块。每个编码帧的宏块可以被分成16*8,8*16和8*8的块。8*8的块又可以进一步分成8*4,4*8和4*4的块。有两个步骤涉及到了决定最后编码的模式。第一步,每个可能模型(block大小)最优的运动矢量MV被计算。匹配准则是典型的绝对误差和(SAD)。第二步,评估每个模式的率失真性能。最优的那个作为最终选择的模式。由于大量的候选模式,H.264/AVC运动估计通常是极复杂和耗时的。虽然提出了很多快速运动估计算法,他们中的大部分需要大量的分支指令,因此可能会变成在CUDA中的性能瓶颈。因此,本文,我们集中于整像素全搜索和四分之一像素提高精度。

        在实现中,一个宏块被分成16个4*4的block,其SAD值采用并行计算对于所有的候选模式运动向量在参考帧的搜索范围内。然后合并4*4的block的SAD值分别形成4*8,8*4,8*4,8*16,16*8和16*16块的SAD。对每个block的大小,我们比较所有候选MV的SAD,据有最小SAD值的是整像素运动矢量(IMV),也就是MV具有整像素精度。下一步,为了获得分像素运动矢量,参考帧通过使用六抽头滤波器插值和双线性滤波器(按照H.264/AVC标准)。在邻近整像素MV附近的24个分像素位置计算SAD,然后选择最小SAD位置作为分像素运动矢量(FMV)。

        前面提到的过程要在每个block上重复,总计算量十分巨大。幸运的是这个算法是基于block的可以很容易的并行化。因此,我们提出了一个完全并行的运动估计(ME)算法,适合CUDA的结构。如第二节讨论,CUDA的性能高度依赖于并行化层次结构,这取决于一个grid中thread block数量和thread block中thread的数量。线程分配对数据分布有很大的影响,它决定了存储转移的大小。增加shared memory的使用可以减少设备DRAM和GPU之间的存储转移,后者是非常耗时间的。

在我们的设计中,我们优化存储器的使用和线程分配/同步来实现运动估计的高效。参考帧和编码帧首先加载到设备DRAM上的纹理存储器中,然后在CUDA上执行并行运动估计算法。计算后数据回传给主机。在最后的讨论中我们使用了4CIF图片大小作为例子,搜索范围假设为32*32。

3.2 4*4的block-sizeSAD计算

        首先计算基本4*4 block的SAD值。一帧中有176*144个block。32*32的搜索范围导致了每个block1024个候选位置(MV??。每个候选的SAD通过一个thread计算,而一个thread block中执行256个threads,如1。因此,thread block的总数是:


每256个候选的4*4 block的SAD值计算被分配到一个thread block中(B1 to B101376 一共101317个thread block),以便于4*4的编码块可以被优先放进shared memory中,贡献256个threads而不用重新读取设备DRAM。存储器的访问数量显著减少。这些步骤以后,所有4*4 block 的SAD值都得到并存储在到设备DRAM中。

3.3 可变块大小的SAD值产生

        图2(a)显示的各种大小block的SAD值可由4*4的SAD值合成得到。因此,我们用4*4的SAD值导出所有大小的block的SAD值。如图2(b)每个thread在候选位置读取sixteen4*4宏块的SAD值,以不同的方式结合它们来形成六个其它的块大小的SAD值。thread block的总数是:



这个步骤以后,不同大小的块的SAD值(对应所有block模式结合共有25个值)在每个thread产生。然后这些值传回DRAM设备。

3.4 整像素SAD值比较

        获得变大小块SAD值以后,一个block的所有1024个SAD值进行比较,最小值作为整像素运动矢量。每个大小的块(从4*4到16*16)具有自己的SAD值比较的kernel;因此,实现并顺序执行了七个kernel以4*4块大小为例,如图3,每个block具有1024个SAD值储存在DRAM设备中,最小的SAD值为最终输出。所有1024个SAD值的比较在一个thread block中完成,其中有256个thread是激活的。换句话,thread block的总数等于一个帧中块的数。首先,每一个thread从DRAM设备中读取4个SAD值并输出最小的SAD。这些临时的SAD值和它们的索引存储在shared memory中。然后,我们激活了128个thread,每个thread比较两个SAD值。较小的那一个存储回shared memory中。在下一次迭代的时候,线程数减半。继续知道获得最优的MV。


        在thread减少的过程中,可能出现两个问题:(1)share memory地址冲突,(2)highly divergent warp(高度离散的wrap)。每一个结果都能使kernel变得无效率。因此,如图4,采用了一个连续地址无离散分支的策略。在每次迭代时,激活的thread ID连续安排来阻止wrap变得离散。另外,数据读取通过striped index(带索引)寻址,其中strip等于激活的线程数。


3.5 分像素插值

        H.264标准支持四分之一像素精度细分运动估计(ME)。为了实现这个任务,参考帧首先经过6抽头滤波器插值来获得半像素图像值,再使用双线性滤波器四分之一像素图像。这个两个滤波器顺序的使用,由于数据的依赖关系,并且实在两个kernel中实现。每个整像素分配到第一个kernel中给的一个thread获得3个半像素,存储回DRAM设备。第二个kernel执行四分之一像素插值,通过读取半像素的值。然后,3个四分之一像素值在每个thread产生,并回存到DRAM设备中。

3.6 分像素运动矢量细化

        在最后一步,运动矢量MV细化步骤是在每一个块源IMV附近寻找最优的分像素运动矢量。设计中,和IMV邻近的24个分像素被检查,如5(a),中间的灰色方块是IMV,白色方块是被检查的半像素,白色圈是被检查的四分之一像素。

一个单独的kernel被分配来执行一个帧中FME的SAD值的计算和比较。因此,24个thread绑在一起作为一个组来产生FMV。每个thread block包含多个组独立地执行来充分利用GPU的能力。首先,优先加载一个编码块到shared memory中,她可以被组中所有的thread读取。每个thread从DRAM中读取不同候选位置的数据。Thread并行计算24个SAD值,临时的存储它们到shared memory中。然后我们激活12个thread,每个thread比较两个SAD值并将较小值存储在shared memory中。在四次迭代之后,最小的SAD值幸存下来,这就是这个块的FMV(分像素运动矢量)。SAD和FMV都回存到DRAM设备中。注意到,最后一次迭代,thread比较三个候选值。最后,GPU产生了最优的MVs并存储回主机PC。

 

4. 实验结果

        介绍平台的硬件与软件配置。提出的算法是全搜索运动估计算法。因此,计算时间根据图片的不同而不同。表1显示了常用MPEG序列(CITY),在搜索范围为32*32的条件下的执行时间。各种SAD值的计算大致消耗中执行时间的59.4%,是所有步骤中最耗时的。因为这一步中DRAM设备的访问和SAD值的计算比其它三步高的多。包括“存储转移”(memory transfer)和“数据与纹理绑定”(texture binding)的其它执行时间为总时间的15.2%。这意味着存储的分配和转移在CUDA的应用中仍然是一个瓶颈。

然后比较只有CPU和GPU与CPU协同工作对算法性能的提升。

5.结论

        在CUDA GPU上提出了有效的块级并行处理算法。GPU作为协处理器可以显著加速计算。未来的工作是将H.264/AVC其它部分也移植到CUDA GPU上来。



0 0