李正平,程洋洋
(安徽大學 電子信息工程學院, 合肥 230039)
現(xiàn)代計算機中內(nèi)存空間都是按照byte劃分的,對于內(nèi)存中的基本數(shù)據(jù)類型的變量,如果它們的起始地址能夠被其自身的大小整除,則將該變量稱為自然對齊數(shù)據(jù),反之稱為非對齊數(shù)據(jù)[1].
不同硬件平臺在非對齊數(shù)據(jù)的處理方面存在很大差異[2].如以MIPS和ARM為代表的精簡指令集計算機對地址要求較為嚴格,需要地址自然對齊;而以X86為代表的復雜指令集計算機就不需要.通常來說,若不按照適合其平臺要求對數(shù)據(jù)存放進行對齊,會在存取效率上帶來損失.比如在MIPS平臺上,如果一個int型數(shù)據(jù)(假設(shè)為32位系統(tǒng))存放在4的整數(shù)倍的地址上,那么一個讀周期就可以讀出;而如果存放在非4的整數(shù)倍的地址上,就可能會需要2個讀周期,并對兩次讀出的結(jié)果的高低字節(jié)進行拼湊才能得到該int型數(shù)據(jù),這顯然會在存取效率上下降很多[3].在龍芯平臺多媒體指令優(yōu)化時,優(yōu)化函數(shù)一旦訪問非對齊數(shù)據(jù)會嚴重抵銷優(yōu)化函數(shù)的性能提升,影響程序的執(zhí)行效率.因此,保證優(yōu)化函數(shù)在數(shù)據(jù)非對齊時也能有較高的性能是一個需要解決的問題.
針對非對齊數(shù)據(jù)存在的問題,許多學者進行了大量研究,提出了許多行之有效的解決方案.其中,IBM公司的Eichenberger A E[4]提出了一種在內(nèi)存非對齊的情況下對循環(huán)進行矢量化處理的方案,即自動重組寄存器中的數(shù)據(jù),以滿足硬件提出的對齊要求;浪潮(北京)電子信息產(chǎn)業(yè)有限公司[5]在初始化時就把非對齊地址根據(jù)映射表中記錄的偏移量糾正為對齊地址,這樣上層應用看到的就全是對齊地址.以上介紹的是基于軟件層面的解決方案,國防科學技術(shù)大學余成龍[6]從硬件上對高性能應用程序的訪存結(jié)構(gòu)進行建模,設(shè)計并實現(xiàn)SIMD分離緩沖行非對齊訪存結(jié)構(gòu)與雙體cache非對齊訪存結(jié)構(gòu)來提高訪存性能等.
以上所提到的方法在非對齊訪存問題上都有一定的效果和意義[7],解決方法是由問題決定的,都服務于各種針對性的問題,本身無優(yōu)劣之分.因此,具體問題還要具體分析[8].針對在龍芯平臺上多媒體指令優(yōu)化過程中遇到的非對齊訪存造成優(yōu)化函數(shù)性能下降的問題,本文提出了接口自適應擇優(yōu)算法.該算法在軟件層面上利用龍芯平臺在指令集層面上設(shè)計的非對齊存取指令,先為這些指令設(shè)計非對齊訪存接口,并在源代碼中根據(jù)程序上下文環(huán)境選擇合適的非對齊訪存接口去存取數(shù)據(jù).最后,以LibYUV庫的多媒體指令優(yōu)化結(jié)果驗證了所提算法在數(shù)據(jù)非對齊的情況下依舊有較高的性能.
龍芯多媒體指令是一套支持基于字節(jié)、半字、字以及雙字整數(shù)并行操作的指令集,能夠加速多媒體運算.龍芯多媒體指令直接使用FPU 的浮點寄存器(64 位,32 個)作為多媒體指令寄存器來實現(xiàn)整數(shù)的并行操作,因此通常用存取浮點類型數(shù)據(jù)的指令來存取需并行計算的整數(shù)[9].
浮點包括單精度和雙精度兩種類型,分別使用4個和8個字節(jié)表示.在龍芯平臺下分別使用lwc1/swc1、ldc1/sdc1(浮點存取指令)來存取浮點類型數(shù)據(jù),其中 lwc1/swc1要求目標數(shù)據(jù)必須是4字節(jié)對齊,ldc1/sdc1要求目標數(shù)據(jù)是8字節(jié)對齊.大多數(shù)情況下,數(shù)據(jù)也確實是按照上述一樣對齊的.但有時可能會錯誤的使用ldc1/sdc1來存取4字節(jié)對齊的浮點數(shù)據(jù),有時甚至傳遞進函數(shù)處理的多媒體數(shù)據(jù)流就是非對齊的,這樣硬件會拋出異常,程序會中斷執(zhí)行;通過操作系統(tǒng)內(nèi)核響應alignment fault異常進入異常處理流程中,把對非對齊的數(shù)據(jù)訪問操作轉(zhuǎn)換為通過多次訪存操作和拼接操作的過程.這個過程由于上下文切換以及引入的多次訪存和拼接操作必然大幅影響程序的執(zhí)行效率[10].
圖1 gslwrc1/gslwlc1指令的數(shù)據(jù)加載過程Fig.1 Data loading process of gslwrc1 / gslwlc1 instruction
對于以上問題,龍芯自定義通用指令gslwrc1/gslwlc1、gsswrc1/gsswlc1、gsldrc1/gsldlc1、gssdrc1/gssdlc1分別在地址非對齊的情況下存取需并行操作的數(shù)據(jù).其中g(shù)slwrc1/gslwlc1表示從非對齊的內(nèi)存地址空間中取字的最低/最高有效部分存放到浮點寄存器中,gsswrc1/gsswlc1表示存儲浮點寄存器中字的低/高有效部分到非對齊的內(nèi)存地址空間.gsldrc1/gsldlc1作用同gslwrc1/gslwlc1,gssdrc1/gssdlc1作用同gsswrc1/gsswlc1,只是操作的目標數(shù)據(jù)是雙字.
現(xiàn)以gslwrc1/gslwlc1指令為例對非對齊訪存指令的數(shù)據(jù)加載過程做出說明.Gslwlc1指令用以從非對齊的內(nèi)存地址空間中取字W的最高有效部分存放到浮點寄存器中,這個地址在內(nèi)存中是以任意字節(jié)為邊界開始的.浮點寄存器$f0中字的最低有效部分(右)則維持不變,浮點寄存器$f0的63..32位是不確定的;同理,gslwrc1指令用以從非對齊的內(nèi)存地址空間中取字的最低有效部分存放到浮點寄存器中,兩條指令結(jié)合使用才能完成從一個非對齊地址讀取一個字數(shù)據(jù).如圖1所示,gslwlc1在這種情況下只加載一個字W的最高字節(jié)到浮點寄存器$f0中,字W的余下低3個字節(jié)則由gslwrc1完成加載.
龍芯下現(xiàn)有的訪存接口名稱分別為:_mm_load_si64、_mm_store_si64、_mm_load_si32和_mm_store_si32.其中,_mm_store_si64的接口實現(xiàn)如下:
extern __inline void FUNCTION_ATTRIBS
_mm_store_si64(__m64 *dest,__m64 src)
{
asm("sdc1 %1,%0 "
:"=m" (*dest)
:"f" (src)
:"memory"
);
}
使用龍芯多媒體指令技術(shù)進行函數(shù)優(yōu)化時,需要函數(shù)參數(shù)地址是自然對齊的,使用現(xiàn)有訪存接口即可完成數(shù)據(jù)的加載和儲存操作.但有時候會出現(xiàn)多媒體指令優(yōu)化后函數(shù)性能不提升反而下降的情況,這一般跟函數(shù)訪問了非對齊地址有關(guān).為了解決這個問題,我們設(shè)計了新的訪存接口.
對于確定為非對齊的參數(shù)地址,利用龍芯非對齊訪存指令,本文分別定義了32位/64位的非對齊訪存接口,用以在多媒體指令優(yōu)化時地址非對齊的時候使用,非對齊訪存接口名稱分別為:_mm_loadu_si64、_mm_storeu_si64、_mm_loadu_si32和_mm_storeu_si32.其中,_mm_storeu_si64接口實現(xiàn)如下:
extern __inline void FUNCTION_ATTRIBS
_mm_storeu_si64(__m64 *dest,__m64 src)
{
asm("gssdlc1 %1,7+%0 "
"gssdrc1 %1,%0 "
:"=m" (*dest)
:"f" (src)
:"memory"
);
}
非對齊訪存接口雖然解決了非對齊訪存性能下降的問題,但兩條非對齊存取指令相對單條浮點存取指令來說執(zhí)行效率依舊較低.有時開發(fā)人員無法確定待訪問地址是否對齊,便無法確定使用現(xiàn)有對齊訪存接口還是非對齊訪存接口.因此需要根據(jù)程序上下文,自動選擇合適的指令接口.基于此,又設(shè)計了自適應多媒體指令訪存接口:_mm_gsload_si64、_mm_gsstore_si64、_mm_gsload_si32和_mm_gsstore_si32.在提高多媒體指令優(yōu)化程序的性能的同時,可以防止花費較長的時間來判斷待訪問地址所使用的訪存指令.其中,_mm_gsstore_si64的接口實現(xiàn)如下:
extern __inline void FUNCTION_ATTRIBS
_mm_gsstore_si64(__m64 *dest,__m64 src)
{
if (!(((long)dest) & 7)) {
asm("sdc1 %1,%0 "
:"=m" (*dest)
:"f" (src)
:"memory"
);
} else {
asm("gssdlc1 %1,7(%0) "
"gssdrc1 %1,0(%0) "
:
:"r" (dest),"f" (src)
:"memory"
);
}
}
非對齊訪存接口可以解決非對齊問題,自適應多媒體指令訪存接口用以解決兩條非對齊指令性能偏低的問題.因此,在具體實踐過程中,若確定地址是4字節(jié)或8字節(jié)對齊的情況下使用現(xiàn)有訪存接口:_mm_load_si32、_mm_store_si32、_mm_load_si64和_mm_store_si64;若不確定地址是否對齊的情況下,且同一地址只進行了一次訪存操作則使用自適應多媒體指令防存接口:_mm_gsload_si32、_mm_gsstore_si32、_mm_gsload_si64和_mm_gsstore_si64;若不確定地址是否對齊且同一地址進行了多次訪存操作則需對地址進行判斷,若地址是4字節(jié)或8字節(jié)對齊,使用接口_mm_load_si32、_mm_store_si32、_mm_load_si64和_mm_store_si64,否則使用接口_mm_loadu_si32、_mm_storeu_si32、_mm_loadu_si64和_mm_storeu_si64.具體算法流程如圖2所示,通過上述操作可使優(yōu)化函數(shù)在地址非對齊時不會出現(xiàn)性能大幅下降的情況.
圖2 接口自適應擇優(yōu)算法流程圖Fig.2 Flow chart of interface adaptive optimization algorithm
LibYUV是Google開源的實現(xiàn)各種YUV與RGB色彩空間之間相互轉(zhuǎn)換、旋轉(zhuǎn)、縮放的庫,并且自帶豐富的測試用例用于保證各個平臺的優(yōu)化在功能和性能上的可靠性.在保證龍芯多媒體指令優(yōu)化功能測試正確的前提下,利用其性能測試來評測多媒體指令優(yōu)化提升的效果[11].在這一過程中,發(fā)現(xiàn)在使用龍芯多媒體指令優(yōu)化后,大多數(shù)測試用例普遍獲得了不錯的性能提升;但有些測試用例出現(xiàn)了性能沒有提升,反而急劇下降的情況[12].經(jīng)過GDB逐條指令分析,確定了這與程序訪問非對齊地址有關(guān),性能下降的測試用例后綴名帶有_unaligned標記,但一些不帶_unaligned標記的測試用例有時也會出現(xiàn)非對齊訪存性能下降問題.從源代碼中分析測試用例的設(shè)計,_unaligned標記的測試用例故意移動了指向待操作數(shù)據(jù)的指針,造成起始地址偏移到非對齊地址上,這是設(shè)計者為了LibYUV的可移植性和可靠性做出的特殊處理.
利用LibYUV的性能測試手段,表1隨機列舉了若干函數(shù)與其對應的測試用例在3種情形下的性能統(tǒng)計數(shù)據(jù),數(shù)值越小代表性能越好.其中“原始函數(shù)”表列代表函數(shù)未優(yōu)化前測試用例的執(zhí)行時間,它是計算性能提升比例的基準值.
(1)
表1中“優(yōu)化函數(shù)應用算法前/提升比例”表列是使用多媒體指令對函數(shù)進行優(yōu)化后測試用例的執(zhí)行時間,以及與基準值的比例,負號表示性能下降,計算方式如公式(1)所示.需要注意的是此處使用多媒體指令進行函數(shù)優(yōu)化,優(yōu)化時按通用做法使用現(xiàn)有訪存接口.如果數(shù)據(jù)是對齊的,優(yōu)化函數(shù)會有普遍的性能提升;如果測試用例需要訪問非對齊地址上的數(shù)據(jù),會造成優(yōu)化函數(shù)性能的下降.
表1中“接口自適應擇優(yōu)算法/提升比例”表列表示在多媒體指令優(yōu)化的基礎(chǔ)上,應用接口自適應擇優(yōu)算法,把先前多媒體指令優(yōu)化的訪存接口按算法邏輯進行簡單替換.即若能確定數(shù)據(jù)是對齊的,多媒體指令優(yōu)化函數(shù)不需要改變,依舊使用現(xiàn)有訪存接口;若不確定數(shù)據(jù)是否對齊且只執(zhí)行一次訪存操作,把訪存接口替換為自適應多媒體指令訪存接口;若確定數(shù)據(jù)是非對齊的,把訪存接口替換為非對齊訪存接口.
圖3用折線圖的形式描述了表1中性能統(tǒng)計數(shù)據(jù)的變化情況.圓點標記的曲線代表多媒體指令優(yōu)化函數(shù)應用接口自適應擇優(yōu)算法前,正方形標記的曲線代表在多媒體指令優(yōu)化的基礎(chǔ)上應用自適應擇優(yōu)算法.
如圖3所示,圓點標記的曲線普遍在零刻度線以下,9個測試用例中有6個出現(xiàn)了性能下降,即在地址非對齊時多媒體指令優(yōu)化函數(shù)性能非但沒有提升反而普遍低于原始函數(shù)的性能.余下的3個測試用例雖然性能得到提升,但提升比例較小,不符合多媒體指令優(yōu)化的預期收益.優(yōu)化后的SobelXRow_C函數(shù)性能下降最為明顯,下降了1.07倍.可見在龍芯平臺上進行多媒體指令優(yōu)化,優(yōu)化函數(shù)訪問非對齊數(shù)據(jù)時會抵消并行計算帶來的性能提升,造成程序性能的下降.
表1 LibYUV的性能統(tǒng)計數(shù)據(jù)表Table 1 Performance statistics of LibYUV
圖3中正方形標記的曲線全部高于圓點標記的曲線,且所有的數(shù)據(jù)都為正值,9個測試用例平均有40%的性能提升,比較符合預期,表明了所提算法在數(shù)據(jù)非對齊的情況下依舊有較高的性能.I422ToYUY2Row_C函數(shù)性能提高比例最大,提升76%,9個函數(shù)最低的也有11%的性能提升.MergeUVRow_C的優(yōu)化函數(shù)性能在應用自適應擇優(yōu)算法前后并未變化,說明此函數(shù)操作的數(shù)據(jù)是自然對齊的.
圖3 優(yōu)化函數(shù)應用接口自適應擇優(yōu)算法前后對比曲線圖Fig.3 Before and after comparison curve of optimization function apply interface adaptive optimization algorithm
在LibYUV庫優(yōu)化過程中,除了使用多媒體指令技術(shù)進行優(yōu)化外,還增加對非對齊訪存問題的處理,并且針對某些特殊運算進行算法級優(yōu)化,比如飽和處理、循環(huán)展開和盡可能消除乘法運算等,取得了不錯的成果.開發(fā)人員在進行多媒體指令優(yōu)化時,可以考慮使用接口自適應擇優(yōu)算法,既能提高優(yōu)化函數(shù)的性能,又能保證優(yōu)化函數(shù)在數(shù)據(jù)非對齊場景下的性能.
論文根據(jù)龍芯處理器的特點,為非對齊訪存指令設(shè)計調(diào)用接口,并提出接口自適應擇優(yōu)算法,解決了龍芯多媒體指令優(yōu)化時地址非對齊造成優(yōu)化函數(shù)性能下降的問題.
論文的另一部分工作是對LibYUV庫使用龍芯多媒體指令進行了全面優(yōu)化,提高了LibYUV在龍芯平臺上的表現(xiàn)能力,相關(guān)工作成果已推送至谷歌LibYUV開源社區(qū).在下一步的工作中,要繼續(xù)使用龍芯多媒體指令完成其它媒體庫的優(yōu)化工作,增強國產(chǎn)計算機平臺上對不同多媒體格式的轉(zhuǎn)換效率.隨著龍芯生態(tài)體系的不斷完善以及應用普及的不斷擴大,國產(chǎn)CPU將會迎來更好的明天.