孟 承,王靜嬌
(中國船舶集團(tuán)有限公司第八研究院,南京 211153)
實(shí)時(shí)性是雷達(dá)信號處理領(lǐng)域一個(gè)不可忽視的指標(biāo),算法的運(yùn)行速度是決定整個(gè)流程性能的關(guān)鍵。典型雷達(dá)的信號處理算法流程通常包括脈沖壓縮、動(dòng)目標(biāo)顯示、恒虛警率檢測[1-2]等。這些算法中包含一些基礎(chǔ)算法,例如三角函數(shù)、快速傅里葉變換、復(fù)數(shù)求模、拷貝等。目前,在ARMv8[3]架構(gòu)的CPU平臺上,除了基本的標(biāo)準(zhǔn)庫外,還有一些開源的算法庫可以使用,如NE10、FFTW、OpenCV[4]等。這些庫函數(shù)可以滿足雷達(dá)信號處理開發(fā)人員對算法正確性以及精度的需求,但是在處理速度上,部分庫函數(shù)如三角函數(shù)、復(fù)數(shù)求模函數(shù)等在大規(guī)模流水使用時(shí)很難達(dá)到性能指標(biāo)。
本文以基礎(chǔ)函數(shù)atan2為例,對其性能進(jìn)行優(yōu)化和硬件平臺實(shí)際測試,并與標(biāo)準(zhǔn)庫函數(shù)進(jìn)行性能對比,其他函數(shù)按類似的優(yōu)化方法,同樣可以得到性能提升。
(1)高速緩存
CPU內(nèi)部與主存之間有一級、二級、三級高速緩存區(qū),其特點(diǎn)是容量依次變大,運(yùn)行速度依次降低,如圖1所示。
圖1 CPU緩存結(jié)構(gòu)
如圖2所示,CPU整個(gè)工作流程包括取指、譯碼、執(zhí)行和數(shù)據(jù)更新。由于CPU特殊的存儲結(jié)構(gòu),指令和數(shù)據(jù)都需要從內(nèi)存中按照三級緩存、二級緩存、一級緩存依次搬運(yùn),數(shù)據(jù)的搬運(yùn)最小單位為塊(通常為64字節(jié)),而不是以字節(jié)為單位[5]。CPU會(huì)先在最快的一級緩存中尋找需要的數(shù)據(jù),如果沒有發(fā)現(xiàn),則繼續(xù)往下一層尋找,以此類推。因此,針對CPU的性能加速可從以下幾個(gè)方面進(jìn)行:單個(gè)指令周期內(nèi)完成盡可能多的操作;數(shù)據(jù)按照內(nèi)存位置順序處理,提高緩存命中率等。
圖2 CPU工作流程
(2)Neon指令集
Neon指令集是單指令多數(shù)據(jù)(Single Instruction Multiple Data,SIMD)技術(shù)在ARM上的實(shí)現(xiàn),利用同一個(gè)命令控制多個(gè)單元以達(dá)到并行處理的效果,提供了寬度為128 bit的向量運(yùn)算。
針對“單個(gè)指令周期內(nèi)完成盡可能多的操作”,本文基于Neon指令集,在ARMv8架構(gòu)CPU上重新實(shí)現(xiàn)基礎(chǔ)函數(shù),以達(dá)到加速效果。
本文的atan2(y,x)功能實(shí)現(xiàn)基于OpenCV開源庫的算法流程進(jìn)行Neon指令重寫,所有使用的函數(shù)皆由arm_neon.h頭文件[6]中定義,主要流程如圖3所示。
圖3 算法主要流程
(1)參數(shù)計(jì)算
ARMv8架構(gòu)CPU提供了32個(gè)128 bit的浮點(diǎn)寄存器,Neon指令集可以操作整個(gè)寄存器。本算法首先計(jì)算一次處理和二次處理的次數(shù)。一次處理每次以4個(gè)浮點(diǎn)寄存器為單位進(jìn)行計(jì)算,即每次對64字節(jié)的數(shù)據(jù)進(jìn)行操作。例如對32位float類型的數(shù)據(jù)進(jìn)行加法操作,調(diào)用8個(gè)浮點(diǎn)寄存器,一次操作即可完成對16個(gè)浮點(diǎn)數(shù)與另外16個(gè)浮點(diǎn)數(shù)的加法計(jì)算。二次處理每次以單個(gè)浮點(diǎn)寄存器為單位進(jìn)行計(jì)算,即每次對16字節(jié)的數(shù)據(jù)進(jìn)行操作,一次操作可以并行計(jì)算4個(gè)浮點(diǎn)數(shù)。最后計(jì)算一次處理和二次處理后的數(shù)據(jù)剩余量,將未處理的長度信息傳遞給剩余處理環(huán)節(jié),一般來說在16個(gè)字節(jié)以內(nèi)。
(2)一次處理
處理流程如圖4所示。
圖4 atan2處理流程
首先是元素導(dǎo)入,利用vld1q_f32函數(shù)將要計(jì)算的數(shù)據(jù)導(dǎo)入到預(yù)先按照數(shù)據(jù)類型聲明好的向量中,便于后續(xù)使用。
其次是條件語句轉(zhuǎn)換,條件跳轉(zhuǎn)是影響緩存不能命中的最大因素,所以這里考慮采用條件語句轉(zhuǎn)換,以增加Cache命中概率,進(jìn)一步提升運(yùn)行性能。本算法進(jìn)行轉(zhuǎn)換時(shí),先計(jì)算出各條件的系數(shù)(符合條件,系數(shù)值為1,否則為0),然后將各條件內(nèi)的計(jì)算與其系數(shù)相乘,最后將所有的結(jié)果相加,例如條件A>B,可使用vcgtq_f32(A,B)函數(shù)計(jì)算該條件的系數(shù)(128位浮點(diǎn)寄存器的返回值由4組32 bit數(shù)據(jù)組成,符合條件時(shí),每組數(shù)據(jù)所有bit位為1,否則為0);其次,使用vshrq_n_u32函數(shù)將系數(shù)返回值以組為單位右移31位,將返回值每組32 bit數(shù)據(jù)的大小轉(zhuǎn)換為1和0兩種,便于后續(xù)計(jì)算。
再次是相關(guān)計(jì)算,根據(jù)頭文件約束的函數(shù)對向量寄存器進(jìn)行加減乘除等運(yùn)算來復(fù)現(xiàn)算法流程,包括向量加(vaddq_f32)、向量除(vdivq_f32)、向量乘(vmulq_f32)等。
最后是元素導(dǎo)出,計(jì)算過程皆在向量寄存器中進(jìn)行,臨時(shí)數(shù)據(jù)或者結(jié)果也保存在向量寄存器中。單次計(jì)算結(jié)束后需要使用vst1q_f32函數(shù)將向量中的結(jié)果數(shù)據(jù)導(dǎo)出至指定的數(shù)據(jù)空間。
(3)二次處理
二次處理的流程與一次處理相同,區(qū)別在于二次處理每次僅處理16字節(jié)的數(shù)據(jù),寄存器使用量是一次處理的四分之一。
(4)剩余處理
在經(jīng)過一次處理和二次處理后,剩余的數(shù)據(jù)長度一般不超過16字節(jié),如果是32位float類型的數(shù)據(jù),此時(shí)元素個(gè)數(shù)不超過4個(gè)。此時(shí)對剩余元素的計(jì)算直接使用OpenCV函數(shù)庫原本的c語言算法實(shí)現(xiàn)即可。
測試環(huán)境如表1所示。
表1 測試環(huán)境
本文對比測試方案如下:
(1)創(chuàng)建float類型的數(shù)據(jù)數(shù)組A、B和C,數(shù)據(jù)為-500到500的隨機(jī)數(shù),測試時(shí)元素長度按照32/64/128/256/512/1 024/2 048/4 096/8 192依次設(shè)定;
(2)每次計(jì)算將數(shù)組中A和B的所有元素進(jìn)行atan2(A,B)運(yùn)算,結(jié)果保存至數(shù)組C中。每次時(shí)間測試進(jìn)行10 000次,然后求每次計(jì)算的耗時(shí)平均值,單位為μs。分別測試兩種計(jì)算方式的耗時(shí),一種為直接使用math.h中定義的atan2函數(shù)計(jì)算,另一種即本文使用neon指令集改寫的算法;
(3)選取1 024元素點(diǎn)的結(jié)果數(shù)據(jù),分別將兩種方式計(jì)算得出的結(jié)果保存至文件,然后由matlab進(jìn)行對比分析。
圖5為耗時(shí)對比圖,表2為具體測試數(shù)據(jù)。可以看出,經(jīng)過本文算法改寫后,atan2函數(shù)的計(jì)算速度提升了1 000%左右。
表2 耗時(shí)測試數(shù)據(jù)
圖5 atan2函數(shù)耗時(shí)對比
由圖6可以看出,本文改編的atan2算法與c標(biāo)準(zhǔn)庫中的atan2函數(shù)計(jì)算結(jié)果基本一致。通過計(jì)算結(jié)果差值與c標(biāo)準(zhǔn)庫計(jì)算的值作比,得出誤差百分比,如圖7所示,可看出結(jié)果誤差比值不超過0.025%。
圖6 atan2函數(shù)結(jié)果對比
圖7 atan2函數(shù)結(jié)果誤差
本文基于ARMv8架構(gòu)CPU平臺,基于Neon指令集,以常用的atan2函數(shù)為例,對其進(jìn)行算法優(yōu)化和性能提升。實(shí)驗(yàn)結(jié)果表明,本文所采用的方式相比于c標(biāo)準(zhǔn)庫對算法的運(yùn)行速度有顯著提升,也能保證一定的精度,同時(shí)該優(yōu)化算法通過相似的步驟也可以推廣到其他基礎(chǔ)函數(shù)中,從而可以提升整個(gè)雷達(dá)信號處理算法的實(shí)時(shí)性。
后續(xù)工作可在兩方面展開:一是可以采用更加靈活、高效的匯編語言或者使用內(nèi)聯(lián)匯編,以尋求更好的性能;二可以按照統(tǒng)一的標(biāo)準(zhǔn)接口規(guī)范,重寫更多的基礎(chǔ)算法,封裝成庫,便于雷達(dá)信號處理開發(fā)人員使用。