JPEG 编解码在 Cell 上的优化

http://www.ibm.com/developerworks/cn/linux/l-cn-celljpegopt/index.html

简介

本文使用的 JPEG 编解码源代码属于 IBM 公司所有。读者如果希望按照本文提到的优化方法来进行 Cell 上 JPEG 编解码的优化,可以使用开源的 JPEG 编解码源代码(下载地址见 参考资料)来作为优化的基础。本文主要是介绍如何在一个 SPU 上优化 JPEG 编解码过程,不涉及将数据切分到不同 SPU 上的问题。

JPEG 编解码过程

本文提到的 JPEG 编码指的是从 bmp 图像文件格式到 jpeg 图像文件格式的转化过程,并且将其划分为如下几个主要部分来说明,如图 1 所示:


图1:JPEG编码过程
JPEG编码过程 

本文提到的JPEG解码指的是从jpeg图像文件格式到bmp图像文件格式的转化过程,并且将其划分为如下几个主要部分来说明,如图 2 所示:


图2:JPEG 解码过程
JPEG 解码过程 

planarToInterleaved这一步指的是将Y、Cb、Cr分开的几段buffer,经过颜色转化后,存储为BGR间隔排列的一段buffer,最后这段buffer会被写入bmp文件,如图3所示:


图3:planarToInterleaved 过程
planarToInterleaved 过程 

具体的JPEG编解码原理见 参考资料

程序移植

  1. 使程序在PPU上正常运行起来。一般来说,适用于x86的源代码,如果在Linux上可以正常运行的话,移植到Cell上基本不需要太多的改动的。但是,因为x86平台是little-endian存储数据的,Cell平台是big-endian的,所以在程序中如果有字节位操作的话,需要做些修改。
  2. 考虑整个程序流程中,哪些部分适合在PPU上执行,哪些部分适合在SPU上执行。我们将文件的读取和写入在PPU上执行,PPU获取到文件buffer和大小后通知SPU,然后由SPU完成编解码过程并将结果buffer写回主存,最后由PPU来写入文件。
  3. 考虑程序是否能够放在只有256K内存的SPU上,如果程序代码量太大,要精简代码或者考虑使用overlay技术。我们初始的源代码具备很多功能,可以做图片的切割、伸缩、颜色分离等等,为了能够移植到SPU上运行,我们将代码精简为处理较为单一的功能,从而最小化代码大小。
  4. 考虑处理的单元块大小是否合适。我们使用的原始代码每次处理的单元块为一个MCU行,和图像的宽度有关。这样,当图片很宽的时候,程序大小会超出256K的限制。所以,我们修改了每次的处理单元块,横向是固定的象素点(而不是和图像的宽度有关),纵向是一个MCU的行数。
  5. 考虑采用何种通信机制。PPU通过signal机制通知SPU开始JPEG编解码;SPU通过中断mailbox机制通知PPU其编解码结束。

图4:在 Cell 上运行的 JPEG 编解码过程
在 Cell 上运行的 JPEG 编解码过程 

性能分析

整个程序的主要部分移植到SPU上后,需要分析目前性能的瓶颈在哪儿,然后开始做具体的优化工作。

我们在程序的各个部分中添加prof_clean、prof_start和prof_stop,使用mambo来分别测出各个部分的CPU cycle数及整个编解码过程的总CPU cycle数(具体操作步骤见 参考资料),这样就可以知道各个部分在整个编解码过程中所占的cycle数比率。

我们刚开始性能分析的结果表明,图1和图2所示的4个部分所占用的cycle数比率加在一起约等于100%,这4个部分就成为我们主要的观察范围。其中,FDCT和IDCT占用最多的cycle数比率,超过了50%。找到瓶颈所在后,再对这个部分做具体的性能分析,由mambo的结果找出不合理的cycle是branch miss、dependency还是channel stall,然后针对性的进行优化。

在不断的性能优化中,需要不断地重新用mambo来做性能分析,因为可能开始的时候某个部分是瓶颈,然后优化完这个部分,另外一个部分会成为瓶颈。我们将FDCT和IDCT做完优化后,其所占的cycle比率降为30%左右,而huffman部分cycle比率变高,成为我们优化的焦点;到后来,bitmapOutput和bitmapInput部分也出现比率较高的channel stall,我们优化了其DMA部分。

总之,在Cell平台上的程序性能优化,需要以性能分析的结果作为优化的依据。

SPU 上的优化

我们用到的优化方式主要有如下几点:

  1. SIMD
  2. 减小branch miss和dependency
  3. double-buffering和multi-buffering

SIMD

一般来说,算法比较规则、数据之间依赖性较小的部分容易做SIMD,我们主要在以下几个方面做了SIMD优化:

1. IDCT的SIMD:

IDCT算法比较规则,适合做SIMD的优化,具体SIMD过程如图5所示。

步骤一:左上角的数据是输入数据,为8x8的矩阵,按照上四行和下四行分为两部分,分别放入两组向量中去。注意,这一步骤中,向量的元素在内存中并不连续,因此需要对原始数据进行转置,这个转置的操作可以在进行反量化的同时进行,这样就不需要额外再操作一次。

步骤二:完成向量的提取后,分别对上半部和下半部向量组进行1维DCT变换(完成行变换),得到右上角的中间矩阵(v,bv)。 因为每个向量中存放四个元素,一次运算可以得到四行的结果。再对中间矩阵按照左半部和右半部得到的两个向量组cv和rcv,分别为左半部和右半部的向量。这个过程,也就是由(v,bv)得到(cv,rcv),等效于进行了转置操作,可以利用如下程序片断,较高效地完成这一操作,不需要任何写回内存的操作。

register vector unsigned char hi = (vector unsigned char){
0x00,0x01,0x02,0x03,0x10,0x11,0x12,0x13,0x04,0x05,0x06,0x07,0x14,0x15,0x16,0x17
};
register vector unsigned char lo = (vector unsigned char){
0x08,0x09,0x0A,0x0B,0x18,0x19,0x1A,0x1B,0x0C,0x0D,0x0E,0x0F,0x1C,0x1D,0x1E,0x1F
};
tempV = spu_shuffle(v0, v2, hi);
tempV_1 = spu_shuffle(v0, v2, lo);
tempV_2 = spu_shuffle(v1, v3, hi);
tempV_3 = spu_shuffle(v1, v3, lo);
cv0 = spu_shuffle(tempV, tempV_2, hi);
cv1 = spu_shuffle(tempV, tempV_2, lo);
cv2 = spu_shuffle(tempV_1, tempV_3, hi);
cv3 = spu_shuffle(tempV_1, tempV_3, lo);

对这两组向量(cv,rcv)再进行1维DCT变换(完成列变换),得到最后的变换结果。

步骤三:步骤二中得到的结果向量,每个向量中只包含四个字节的有效数据,直接存储到内存中去会比较低效,因此将一行中相邻的8个字节压缩到一个向量中去,再进行后续的操作会更高效。

步骤四:将向量依次写入内存。


图5:IDCT 的 SIMD
IDCT 的 SIMD 

2. FDCT的SIMD:

FDCT算法是IDCT的逆算法,SIMD的过程也基本是IDCT的逆过程。所不同之处是,对于IDCT而言,输入数据来自于Huffman解码后的数据,需要反量化处理后才能进行运算,运算完成后得到的就是颜色空间中的数据了,一般就是YCbCr分量的值;而对于FDCT来说,输入来自于YCbCr的原始数据,输出则需要进行量化运算。考虑到量化运算本身也具有一定的计算复杂度,IDCT与FDCT在起始部分与结尾部分向量的处理要有所区别。

3. bitmapInput的SIMD:

bitmapInput在JPEG的编码过程中实现了颜色转换(数据从BGR色系到YCbCr色系)和数据分离(将BGR排列的数据转化为Y、Cb、Cr分开的数据,便于做后面的FDCT),其具体SIMD过程如图6所示:


图6:bitmapInput 的 SIMD
bitmapInput 的 SIMD 

4. planarToInterleaved的SIMD:

planarToInterleaved在JPEG的解码过程中实现了颜色转换(数据从YCbCr色系到BGR色系)和数据组合(将Y、Cb、Cr分开的数据组合为BGR排列的数据,最后写到bmp格式的文件中),其具体SIMD过程如图7所示:


图7:planarToInterleaved 的 SIMD
planarToInterleaved 的 SIMD 

减小 branch miss 和 dependency

在各个部分的CPU cycle结果中,当branch miss和dependency的cycle超过20%比率的时候,就应该考虑减小branch miss和dependency,,我们主要在以下几个方面做了优化:

1. huffmanDecoder

解码过程中需要判别是否为0xFF的字节,然后忽略紧随的0x00。假定JPEG文件的数据区域段中0xFF均为实际的图像数据,可以进行如此优化:对于压缩过的bit流,每次提取64个bits到一个vector中,64个bit是8个bytes,因此用一个256个单元的表就可以表示0xFF的分布状况,可以先计算出这样一个表,再利用向量运算直接查表即可完成对0xFF的处理。这样可以完全消除其过程中的判断语句,从而减小branch miss。

2. 其他减小branch miss的方法:

我们在头文件中定义了如下的宏来做branch hint:

#define unlikely(_c) __builtin_expect((_c), 0)
#define likely(_c) __builtin_expect((_c), 1)

然后,将所有的判断返回值错误的地方,如下代码:

if( returnCode != 0 )
{
        return returnCode;
}

改为:

if( unlikely(returnCode != 0) )
{
    	return returnCode;
}

当某个条件大多数时候总是成立的时候,则使用likely来做branch hint。

double-buffering 和 multi-buffering

当性能测试的结果表明,channel的cycle比率较高的时候,需要考虑double-buffering或者multi-buffering。这时候,程序的数据DMA传输时间大于数据处理时间,需要减小等待数据DMA的时间。double-buffering是multi-buffering的一种形式,当使用两段buffer的时候,称为double-buffering。我们主要在如下几个方面做了multi-buffering的处理:

1. bitmapInput的double-buffering:

bitmapInput在JPEG的编码过程中实现了bmp图片的数据DMA读取,其double-buffering过程如图 8 所示:


图8:bitmapInput 的 double-buffering
bitmapInput 的 double-buffering 

2. bitmapOutput的multi-buffering:

bitmapOutput在JPEG的解码过程中实现了bmp图片的数据DMA写出,其multi-buffering过程和double-buffering基本相似。DMA写出的时候,分配了更多的buffer作为数据的写出缓冲,其过程如图9所示:


图9:bitmapOutput 的 multi-buffering
bitmapOutput 的 multi-buffering 

结果分析

经过上述几个方面在SPU上的优化,和最初移植到Cell上的代码相比,JPEG的编解码性能提高很大,JPEG编码在一个SPU上优化的情况如图10所示,JPEG解码在一个SPU上优化的情况如图11所示:


图10:JPEG 编码优化代码和原始代码性能相比
JPEG 编码优化代码和原始代码性能相比 

图11:JPEG 解码优化代码和原始代码性能相比
JPEG 解码优化代码和原始代码性能相比 

由此可见,移植到Cell上的代码需要做一些特定的优化(特别是SIMD)后,才能达到较好的性能。

JPEG编码在一个SPU上达到的性能及cycel分布(由mambo测得)如表1所示,JPEG解码在一个SPU上达到的性能及cycel分布如表2所示:


表1:JPEG编码在一个SPU上的性能
表1:JPEG编码在一个SPU上的性能 

表2:JPEG解码在一个SPU上的性能
表2:JPEG解码在一个SPU上的性能 

*:图片使用的是EEMBC bechmark中的测试图片,每秒帧数也是由EEMBC bechmark测得(参见参考资料)。

由两个表格结果可以看出,优化后的cycle分布是比较合理的,dual cycle在20%左右,branch miss在10%左右,dependency在20%左右,channel stall控制在3%之内。各个cycel的分布和程序本身的算法比较相关,通过上面的两个表格可以看出,JPEG解码过程中的dependency cycel较高,这与解码算法中数据之间依赖性很大有关。Intel在其官方的IPP性能测试白皮书(参见 参考资料)中提到了JPEG编解码的速度,可以看出,一个SPU的性能可以与一个intel的P4相当。一个Cell芯片有8个SPU,并行处理数据的话,可以达到P4速度的8倍。

结束语

通过对于JPEG编解码在Cell上优化,我们发现这个独特的CPU的确具有很好的性能。这种异构的CPU使得资源调配的空间变大,我们可以利用一个SPU来做JPEG编解码,同时用另外的SPU来做别的计算处理工作。独特的SPU架构使得程序员有很大的空间来做性能的优化,充分发挥SIMD的优势。随着Cell上相应编程工具、编程框架、性能检测工具、和编译器的完善和改进,Cell上的编程和优化工作会更加简单和高效。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值