許 菁,黃 震,韓向清,付 林
(中國(guó)船舶集團(tuán)有限公司第八研究院,南京 211153)
在雷達(dá)信號(hào)處理或數(shù)據(jù)處理中,當(dāng)使用PPC的RapidIO進(jìn)行數(shù)據(jù)傳輸時(shí),通常采用門鈴來實(shí)現(xiàn)數(shù)據(jù)傳輸完成后的消息中斷。對(duì)于這樣的門鈴中斷函數(shù),需要盡可能快地完成數(shù)據(jù)轉(zhuǎn)移,以避免多次傳輸導(dǎo)致來不及處理的數(shù)據(jù)丟失。一般在進(jìn)行大量不確定數(shù)量數(shù)據(jù)傳輸時(shí),會(huì)在數(shù)據(jù)頭中設(shè)計(jì)一個(gè)字段來描述當(dāng)次傳輸?shù)臄?shù)量?,F(xiàn)有處理方法會(huì)通過內(nèi)存復(fù)制函數(shù)(memcpy)將數(shù)據(jù)頭復(fù)制一個(gè)副本,然后對(duì)副本中的長(zhǎng)度字段進(jìn)行讀取。
本文對(duì)現(xiàn)有處理方法進(jìn)行優(yōu)化,以減少使用內(nèi)存復(fù)制函數(shù)(memcpy)的應(yīng)用,提高輸出讀取的效率。
在PowerPC的RapidIO數(shù)據(jù)傳輸時(shí),需要進(jìn)行內(nèi)存地址映射,讀取傳輸?shù)臄?shù)據(jù)是通過直接訪問內(nèi)存地址實(shí)現(xiàn)的。
編譯器實(shí)現(xiàn)直接(或間接)地址訪問讀取數(shù)據(jù),可以表達(dá)為
MOV AX,[Address]
(1)
而對(duì)結(jié)構(gòu)體的字段訪問表達(dá)為
MOV AX,[Address + offset]
(2)
式中,Address為結(jié)構(gòu)體的首地址;offset為字段相對(duì)首地址的偏移量。
采用內(nèi)存復(fù)制函數(shù)(memcpy)復(fù)制數(shù)據(jù)頭的副本,實(shí)際上是對(duì)式(1)、跳轉(zhuǎn)語(yǔ)句(jnz)以及計(jì)數(shù)器的多次調(diào)用,它主體的匯編表達(dá)將如下所示:
10:MOV CX,Count
20:MOVEAX,[Address]
30:MOV [DST],EAX
40:INC Address
(3)
50:INC DST
60:DEC CX
70:JNZ 20
同時(shí),采用內(nèi)存復(fù)制函數(shù)(memcpy)還將帶來函數(shù)開銷,如寄存器保存、函數(shù)堆棧等操作,這將涉及ESP、EBP等棧寄存器和EAX、ECX、EDX等調(diào)用者寄存器及EBX、ESI、EDI等被調(diào)用者寄存器的操作和保存。
首先,調(diào)用內(nèi)存復(fù)制函數(shù)(memcpy)時(shí),需要先將EAX、ECX、EDX等調(diào)用者寄存器進(jìn)行保存:
10:PUSH EAX
20:PUSH EBX
30:PUSH ECX
其次,需要將輸入給內(nèi)存復(fù)制函數(shù)(memcpy)的參數(shù),入棧后推送給內(nèi)存復(fù)制函數(shù)(memcpy):
10:MOV EAX,[Address1]
20:PUSH EAX
30:MOV EAX,[Address2]
40:PUSH EAX
然后,內(nèi)存復(fù)制函數(shù)(memcpy)執(zhí)行式(3)所示的函數(shù)主體;
再者,內(nèi)存復(fù)制函數(shù)(memcpy)將執(zhí)行結(jié)果入棧:
10:MOV EAX,[Address3]
20:PUSH EAX
最后,調(diào)用函數(shù)將棧中結(jié)果出棧,并還原保存的寄存器:
10:POP EAX
20:MOV[Address4],EAX
30:POP EAX
40:POP EAX
50:PUSH ECX
60:PUSH EBX
70:PUSH EAX
上述開銷將占用大量的CPU時(shí)間,對(duì)于門鈴響應(yīng)來說會(huì)造成較長(zhǎng)時(shí)間的延遲,如果在此時(shí)間內(nèi)有新的接口操作和門鈴響應(yīng),就會(huì)造成數(shù)據(jù)的丟失。
從式(1)、(2)的描述可以看出,盡管C語(yǔ)言程序的寫法不一致,但是實(shí)際上對(duì)內(nèi)存的訪問方法是統(tǒng)一的。因此,只要通過強(qiáng)制類型的轉(zhuǎn)換實(shí)現(xiàn)對(duì)應(yīng)數(shù)據(jù)字段的訪問,就可以達(dá)到同樣的目的。方法如下式所示:
int datalen=(([datastruct]*)address)->[lenfield]
(4)
式中,[datastruct]為待轉(zhuǎn)換結(jié)構(gòu)體類型描述字;[datastruct]*為該類型的指針;address為數(shù)據(jù)首地址;[lenfield]為描述數(shù)據(jù)長(zhǎng)度的字段名。
式(4)實(shí)現(xiàn)了一個(gè)地址描述變量到結(jié)構(gòu)體類型指針的轉(zhuǎn)換以及對(duì)結(jié)構(gòu)體字段的訪問,從表達(dá)上看,是一個(gè)相對(duì)復(fù)雜的過程,可能也將占用一定的CPU開銷。但是從編譯器的表達(dá)上看,則恰恰相反,由于這個(gè)類型指針的轉(zhuǎn)換過程及對(duì)結(jié)構(gòu)體字段的訪問的表達(dá)將在編譯階段完成,實(shí)際的有效運(yùn)行指令將極為簡(jiǎn)單,示意如下式所示:
10:MOV EAX,[Address + offset]
20:MOV [DST],EAX
(5)
可以看出:對(duì)地址描述變量到結(jié)構(gòu)體類型指針的轉(zhuǎn)換以及對(duì)結(jié)構(gòu)體字段的訪問,通過編譯過程就僅僅表達(dá)為首地址和偏移量的表達(dá),兩個(gè)過程被濃縮成一條指令,大大減少了實(shí)際有效運(yùn)行指令數(shù),將極大地提高運(yùn)行效率。
在驗(yàn)證程序中,設(shè)計(jì)百萬次同樣操作的循環(huán),分別采用內(nèi)存復(fù)制函數(shù)(memcpy)和本文設(shè)計(jì)方法來讀取一個(gè)字段,最終得到的數(shù)據(jù)如表1所示。
表1 運(yùn)行時(shí)間比較
可以看出:本文方法所需的運(yùn)行時(shí)間遠(yuǎn)遠(yuǎn)優(yōu)于內(nèi)存復(fù)制函數(shù)(memcpy),運(yùn)行效率提高了一個(gè)數(shù)量級(jí),將極大節(jié)省CPU的運(yùn)行開銷,減少門鈴響應(yīng)函數(shù)的響應(yīng)時(shí)間,降低由于內(nèi)存搬移時(shí)間過長(zhǎng)而導(dǎo)致新一次數(shù)據(jù)覆蓋上一次有效數(shù)據(jù)的幾率。
本文研究了采用地址描述變量到結(jié)構(gòu)體類型指針轉(zhuǎn)換以及對(duì)結(jié)構(gòu)體字段訪問替代內(nèi)存復(fù)制函數(shù)(memcpy)的方法。經(jīng)試驗(yàn)測(cè)試,該方法可有效提高運(yùn)行效率,降低數(shù)據(jù)傳輸中數(shù)據(jù)被覆蓋而導(dǎo)致丟失的幾率,可以在基于PowerPC的雷達(dá)信號(hào)、數(shù)據(jù)處理軟件中推廣,具有一定的實(shí)用價(jià)值。