何頌頌 彭 飛 林傳文 陶劍文
1(寧波職業(yè)技術(shù)學(xué)院電子信息工程學(xué)院 浙江 寧波 315800) 2(龍芯中科(合肥)技術(shù)有限公司 安徽 合肥 230088) 3(合肥學(xué)院人工智能與大數(shù)據(jù)學(xué)院 安徽 合肥 230601)
Libjpeg[1]是一個(gè)被廣泛使用的JPEG圖像壓縮庫,Libjpeg-turbo[2]是Libjpeg的一個(gè)分支,它使用SIMD指令來加速標(biāo)準(zhǔn)型JPEG的編碼和解碼。許多項(xiàng)目現(xiàn)在使用Libjpeg-turbo而不是Libjpeg,包括流行的GNU/Linux發(fā)行版(Fedora,Debian,Mageia,openSUSE,…)、Mozilla和Chrome。Libjpeg-turbo支持X86、x86- 64、ARM和PowerPC系統(tǒng)的SIMD加速,但不支持龍芯系統(tǒng)的SIMD加速。
龍芯3A是我國(guó)自主研發(fā)的通用處理器芯片,片內(nèi)擁有四個(gè)64位的四發(fā)射超標(biāo)量GS464e處理器核,支持SIMD加速[3-4]。為了保障國(guó)家的信息安全,我們有必要推廣龍芯等國(guó)產(chǎn)CPU的應(yīng)用。但是由于指令集的差異性,龍芯處理器上的操作系統(tǒng)和基礎(chǔ)軟件的遷移仍然存在一些問題,需要開發(fā)者逐一去解決[5]。為了能夠吸引更多的開發(fā)者在龍芯平臺(tái)上進(jìn)行開發(fā),需要實(shí)現(xiàn)操作系統(tǒng)和基礎(chǔ)軟件的移植與優(yōu)化,并建立一個(gè)完整的產(chǎn)業(yè)鏈[6]。國(guó)內(nèi)已有部分研究人員針對(duì)龍芯體系結(jié)構(gòu)做了一些關(guān)于操作系統(tǒng)和基礎(chǔ)軟件的性能優(yōu)化工作。
在操作系統(tǒng)方面,文獻(xiàn)[7]針對(duì)龍芯3A的Linux系統(tǒng)給出了基于預(yù)留核調(diào)度的實(shí)時(shí)優(yōu)化方法,并使用新設(shè)計(jì)的一種基于PCI板內(nèi)部定時(shí)器的測(cè)試方法對(duì)其進(jìn)行了測(cè)試,結(jié)果表明該系統(tǒng)能夠滿足毫秒級(jí)的實(shí)時(shí)應(yīng)用需求。文獻(xiàn)[8]對(duì)龍芯3A最重要的操作系統(tǒng)和編譯器做了性能分析和優(yōu)化。文獻(xiàn)[9]使用優(yōu)化過的GCC編譯器重新編譯了龍芯3A1500上Fedora21操作系統(tǒng),使操作系統(tǒng)性能得到了一定提升。
在基礎(chǔ)軟件方面,文獻(xiàn)[10]將Docker移植到了龍芯Fedora28系統(tǒng)中。文獻(xiàn)[11]完成了氣象預(yù)報(bào)展示系統(tǒng)所需的Vis5D和GrADS軟件的移植工作。文獻(xiàn)[12]將兩種參考基因組壓縮算法移植到了龍芯平臺(tái),并采用多進(jìn)程技術(shù)提高了壓縮速度。文獻(xiàn)[13]基于龍芯體系結(jié)構(gòu)對(duì)標(biāo)準(zhǔn)函數(shù)庫進(jìn)行了優(yōu)化,并給出一些程序優(yōu)化方法。文獻(xiàn)[14-15]對(duì)基礎(chǔ)線性代數(shù)子程序庫(BLAS)進(jìn)行了優(yōu)化,性能提高1倍以上。文獻(xiàn)[16]將ffmpeg移植到了龍芯3B系統(tǒng)上,并做了向量化處理。文獻(xiàn)[17]基于龍芯SIMD技術(shù)對(duì)H.264視頻解碼器進(jìn)行了優(yōu)化。文獻(xiàn)[18]對(duì)二進(jìn)制翻譯器進(jìn)行了優(yōu)化,使龍芯3A平臺(tái)上翻譯運(yùn)行的操作系統(tǒng)運(yùn)行速度得到了提升。這些文章都對(duì)龍芯平臺(tái)上的基礎(chǔ)軟件做了很好的優(yōu)化工作,但至今沒有人對(duì)Libjpeg進(jìn)行過優(yōu)化。
為了提高國(guó)產(chǎn)龍芯系統(tǒng)桌面應(yīng)用的性能,尤其是瀏覽器加載圖片的性能,本文提出Libjpeg-turbo庫針對(duì)龍芯3A的優(yōu)化方法。在優(yōu)化過程中,除了用到了龍芯3A的64位SIMD指令,還用到了循環(huán)展開、指令調(diào)度等常規(guī)代碼優(yōu)化方法。
JPEG壓縮過程中,每個(gè)圖像塊會(huì)經(jīng)過離散余弦變換(DCT)和量化處理。通過這兩個(gè)步驟,所有數(shù)據(jù)都會(huì)變成較小的整數(shù),而大部分?jǐn)?shù)據(jù)都會(huì)變成零,從而提高哈夫曼編碼的壓縮率。反之解壓過程中,就需要進(jìn)行反量化處理和反離散余弦變換(IDCT)。式(1)給出了IDCT的計(jì)算方法,其中F(u,v)表示反量化后的矩陣,其中:u和v分別是其行號(hào)和列號(hào),f(x,y)則表示IDCT變換后的結(jié)果矩陣,其中x、y分別是其行號(hào)和列號(hào)。
(1)
為了減少IDCT的重復(fù)計(jì)算量,IDCT在Libjpeg-turbo的實(shí)現(xiàn)中[19]被分為兩個(gè)步驟:1) 對(duì)輸入數(shù)據(jù)進(jìn)行逐列處理,并存儲(chǔ)到工作數(shù)組中,為了減少誤差,還對(duì)輸出結(jié)果做了2的N次方的放大;2) 處理工作數(shù)組中的行,并存儲(chǔ)到輸出數(shù)組中,并將結(jié)果按比例縮小,撤銷第一步對(duì)結(jié)果2的N次方的放大。
由于輸入矩陣內(nèi)的數(shù)據(jù)都是short類型的,需要占用2個(gè)字節(jié)的空間,而龍芯3A的寄存器是64位的,所以一次可以從內(nèi)存中取4個(gè)數(shù)據(jù)。同樣,量化矩陣的數(shù)據(jù)也可以一次性取4個(gè)。然后就可以使用SIMD的乘運(yùn)算指令來完成4個(gè)數(shù)據(jù)的反量化計(jì)算。IDCT的運(yùn)算過程會(huì)用到余弦函數(shù),其計(jì)算過程非常耗時(shí)。所以需要?jiǎng)?chuàng)建一個(gè)余弦函數(shù)表,通過查表來計(jì)算余弦函數(shù)。最后,還使用SIMD的算術(shù)右移指令把結(jié)果批量縮放回去。
常用的YUV格式有YCbCr4 ∶4 ∶4、YCbCr4 ∶2 ∶2、YCbCr4 ∶2 ∶0和YCbCr4 ∶1 ∶1,分別表示完全取樣、2 ∶1的水平取樣(垂直完全取樣)、2 ∶1的水平和垂直取樣、4 ∶1的水平取樣(垂直完全取樣)。除了YCbCr4 ∶4 ∶4,其他三種取樣方式都存在共用CbCr的情況,即一個(gè)Cb或Cr會(huì)被多個(gè)像素點(diǎn)引用。因此,在解壓過程中,需要把Cb和Cr做上采樣處理,即編碼格式要將Cb和Cr填充到每個(gè)像素點(diǎn)中。
常見的色度上采樣方式有盒式濾波(box filter)和三角形濾波(triangle filter)兩種,在Libjpeg-turbo中都有對(duì)應(yīng)的實(shí)現(xiàn)。色度盒式濾波上采樣的代碼實(shí)現(xiàn)都比較簡(jiǎn)單,只需要逐行遍歷輸入矩陣,然后復(fù)制多份數(shù)據(jù)到輸出矩陣對(duì)應(yīng)的位置中。色度三角形濾波上采樣的代碼實(shí)現(xiàn)則復(fù)雜一些,需要通過周圍的數(shù)據(jù)來計(jì)算當(dāng)前位置的數(shù)據(jù)。后者的復(fù)雜度已經(jīng)無法使用編譯器來完成優(yōu)化工作,只能通過手動(dòng)編寫專用的版本來實(shí)現(xiàn)性能的提升。
基于色度三角形濾波上采樣的數(shù)據(jù)級(jí)并行特性,針對(duì)循環(huán)結(jié)構(gòu)進(jìn)行橫向循環(huán)展開操作,然后使用SIMD操作并行處理,每次處理行內(nèi)連續(xù)8個(gè)數(shù)據(jù)。因?yàn)閷?shí)際運(yùn)算過程中,需要用到上一行和下一行的數(shù)據(jù),所以每次必須加載24個(gè)數(shù)據(jù)。核心計(jì)算的偽代碼如下,其中:mm0、mm1和mm2分別是當(dāng)前行、上一行和下一行的8字節(jié)向量;mm3為全零8字節(jié)向量;_mm_unpacklo_pi8和_mm_unpackhi_pi8分別是對(duì)包數(shù)據(jù)進(jìn)行取低位解包和取高位解包,將一個(gè)寄存器里存的連續(xù)8個(gè)數(shù)據(jù)轉(zhuǎn)存到兩個(gè)寄存器中,每個(gè)寄存器里存4個(gè)半字為單位的數(shù)據(jù);_mm_mullo_pi16是對(duì)有符號(hào)半字包數(shù)據(jù)進(jìn)行乘運(yùn)算;PW_THREE是由4個(gè)半字?jǐn)?shù)值3組成的8字節(jié)向量;_mm_add_pi16是以半字為一個(gè)包數(shù)據(jù),對(duì)數(shù)據(jù)同時(shí)相加。
mm4=mm0;
mm0=_mm_unpacklo_pi8(mm0,mm3);
mm4=_mm_unpackhi_pi8(mm4,mm3);
mm5=mm1;
mm1=_mm_unpacklo_pi8(mm1,mm3);
mm5=_mm_unpackhi_pi8(mm5,mm3);
mm6=mm2;
mm2=_mm_unpacklo_pi8(mm2,mm3);
mm6=_mm_unpackhi_pi8(mm6,mm3);
mm0=_mm_mullo_pi16(mm0,PW_THREE);
mm4=_mm_mullo_pi16(mm4,PW_THREE);
mm1=_mm_add_pi16(mm1,mm0);
mm5=_mm_add_pi16(mm5,mm4);
mm2=_mm_add_pi16(mm2,mm0);
mm6=_mm_add_pi16(mm6,mm4);
很多原始圖片數(shù)據(jù)都是以RGB格式存儲(chǔ)的,但是JPEG壓縮算法處理的是YUV格式的數(shù)據(jù),所以在壓縮之前需要做顏色空間轉(zhuǎn)換,將RGB格式轉(zhuǎn)換成YUV格式。顏色空間轉(zhuǎn)換快速實(shí)現(xiàn)方式有整形算法、部分查表法和完全查表法。這三種算法在傳統(tǒng)處理器上能夠很有效地提高運(yùn)算速度,但是無法在支持SIMD指令的處理器上實(shí)現(xiàn)并行化。因此,為了進(jìn)一步提高性能,我們使用SIMD指令對(duì)原始算法進(jìn)行并行化處理。我們實(shí)現(xiàn)的轉(zhuǎn)換方式如式(2)所示,其中CJ是用于簡(jiǎn)化正確的輸入,在8位實(shí)現(xiàn)中定義為128,在12位實(shí)現(xiàn)中定義為2 048。
(2)
使用SIMD指令運(yùn)算前,先對(duì)數(shù)據(jù)進(jìn)行加載和預(yù)處理。RGB數(shù)據(jù)在內(nèi)存中是按組連續(xù)存儲(chǔ)的,每組占用3字節(jié)或4字節(jié)內(nèi)存。因此,在運(yùn)算之前先要將數(shù)據(jù)進(jìn)行重組,使同色數(shù)據(jù)在同一個(gè)寄存器中。以3字節(jié)RGB數(shù)據(jù)為例,如圖1所示,需要經(jīng)過6個(gè)步驟完成數(shù)據(jù)預(yù)處理:
圖1 RGB轉(zhuǎn)YUV數(shù)據(jù)加載與預(yù)處理
(1) 通過ldc1指令將8組RGB數(shù)據(jù)加載到3個(gè)浮點(diǎn)寄存器中(小尾端)。
(2) 通過邏輯移位指令dsll和dsrl,將寄存器里的數(shù)據(jù)左移或右移32位。
(3) 使用解包指令punpckhbh和punpcklbh,分別將兩個(gè)寄存器的高位數(shù)據(jù)或兩個(gè)寄存器的低位數(shù)據(jù)進(jìn)行合并。
(4) 通過邏輯移位指令dsll和dsrl,將解包后的數(shù)據(jù)左移或右移32位。
(5) 再次使用解包指令punpckhbh和punpcklbh,分別將兩個(gè)寄存器的高位數(shù)據(jù)或兩個(gè)寄存器的低位數(shù)據(jù)進(jìn)行合并。
(6) 使用解包指令punpckhbh和punpcklbh,將寄存器與一個(gè)全零寄存器進(jìn)行高位數(shù)據(jù)或低位數(shù)據(jù)合并,從而將單字節(jié)數(shù)據(jù)擴(kuò)展為雙字節(jié)數(shù)據(jù),并使一個(gè)寄存器中只存一種顏色數(shù)據(jù)。
接下來只需要按照公式進(jìn)行乘加和乘減等運(yùn)算就可以把Y、Cb和Cr計(jì)算出來。
實(shí)驗(yàn)的硬件平臺(tái)是基于龍芯3號(hào)系列處理器的最新產(chǎn)品型號(hào)3A3000,主頻1.45 GHz,內(nèi)存4 GB。龍芯3A3000具有四核64位內(nèi)核、64位浮點(diǎn)單元和四發(fā)射亂序執(zhí)行GS464E微處理結(jié)構(gòu)。操作系統(tǒng)為64位Loongnix系統(tǒng)。原始版本是Libjpeg-turbo 1.5.3,優(yōu)化后的代碼已經(jīng)合并提交到Libjpeg-turbo 1.5.90。
本文使用Libjpeg-turbo自帶的tjbench來進(jìn)行正確性測(cè)試,如表1所示,12個(gè)測(cè)試用例優(yōu)化前后的MD5值都一致,即正確性測(cè)試通過。
性能測(cè)試則是使用tjbench分別調(diào)用優(yōu)化前后的代碼,然后比較它們之間幀率的差異。測(cè)試圖片分別是8比特灰度圖、16比特灰度圖、16比特線性灰度圖、8比特RGB圖、16比特RGB圖和16比特線性RGB圖,分辨率都是4 256×2 848。
反離散余弦變換的優(yōu)化,性能測(cè)試命令是./tjbench$TEST_BITMAP 95-yuv,測(cè)試結(jié)果如圖2所示。可以看出,性能提升最少的是色度零抽樣,優(yōu)化后的解碼幀率是優(yōu)化前的1.3倍左右。色度抽樣為4 ∶4 ∶4時(shí)性能提升最明顯,優(yōu)化后的解碼幀率是優(yōu)化前的2.1倍以上。
圖2 反離散余弦變換優(yōu)化效果
上采樣的優(yōu)化效果如圖3所示,因?yàn)樯攘愠闃雍蜕? ∶4 ∶4并不需要做上采樣,所以沒有任何效果,而對(duì)于色度抽樣4 ∶2 ∶0,采樣幀率提高到將近6倍,色度抽樣4 ∶2 ∶2的采樣幀率提高到將近7倍。
圖3 上采樣優(yōu)化效果
針對(duì)不同的色度抽樣,JPEG編碼幀率提高到原來的2倍左右,如圖4-圖7所示。在龍芯3A處理器上,SIMD向量指令一次可以處理4個(gè)數(shù)據(jù),因此理論加速度應(yīng)該是4倍。但實(shí)際測(cè)試和理論加速度還是有一定差距,主要原因是為了能夠使用SIMD指令進(jìn)行并行化運(yùn)算,前期需要做一些數(shù)據(jù)預(yù)處理,這些數(shù)據(jù)預(yù)處理指令需要消耗一些時(shí)間。另外,不是所有運(yùn)算都適合做并行化的,所以性能提升無法達(dá)到理論值。
圖4 編碼整體優(yōu)化效果Gray
圖5 編碼整體優(yōu)化效果4 ∶2 ∶0
圖6 編碼整體優(yōu)化效果4 ∶2 ∶2
圖7 編碼整體優(yōu)化效果4 ∶4 ∶4
JPEG解碼優(yōu)化對(duì)色度零抽樣的性能提升并不明顯,只有30%左右,如圖8所示。而對(duì)其他色度抽樣的性能提升非常明顯,都有將近4倍的加速,如圖9-圖11所示。解碼的并行化效果優(yōu)于編碼的原因是解碼的數(shù)據(jù)預(yù)處理比編碼簡(jiǎn)單。
圖8 解碼整體優(yōu)化效果Gray
圖9 解碼整體優(yōu)化效果4 ∶2 ∶0
圖10 解碼整體優(yōu)化效果4 ∶2 ∶2
圖11 解碼整體優(yōu)化效果4 ∶4 ∶4
圖片數(shù)據(jù)是目前網(wǎng)絡(luò)上傳輸最多的數(shù)據(jù)類型之一,受到網(wǎng)絡(luò)傳輸和存儲(chǔ)容量的限制,圖片的高效編解碼是必不可少的。由于工藝的限制,國(guó)產(chǎn)處理器的主頻一般都會(huì)相對(duì)偏低一些,因此在國(guó)產(chǎn)處理器上提高圖片的編解碼效率是非常重要的。本文的成果已經(jīng)在Libjpeg開源社區(qū)發(fā)布,這將有利于龍芯3A計(jì)算機(jī)在國(guó)內(nèi)桌面機(jī)電腦市場(chǎng)的推廣,對(duì)其他處理器的指令級(jí)并行化優(yōu)化也有一定的指導(dǎo)意義。國(guó)產(chǎn)處理器的發(fā)展需要操作系統(tǒng)和基礎(chǔ)軟件的支持,而瀏覽器是現(xiàn)在桌面電腦上使用頻率最高的軟件,下一步我們將對(duì)Chrome和Firefox瀏覽器進(jìn)行深度定制優(yōu)化,使龍芯3A系列計(jì)算機(jī)上的瀏覽器性能得到有效提升。