arm彙編優化
要做程式的優化,最徹底的方法當然是彙編!還有除了彙編以外(除了二進位制)能讓你對你的處理器有更全面的控制嗎?!對於arm彙編,作為乙個初學者,也就只好先補補基礎了@_@。
首先,程式段的定義從area 開始,它命名乙個**區域,注意,用非阿拉伯數字作為名字時,應該用|把名字包起來,code關鍵字宣告程式(猜測),readonly宣告訪問許可權(猜測)。export 來表示某個可以用作外部連線的符號(簡單點,應該就是函式名?)。end用來結尾。
#eg:
area |.text|, code, readonly
export square
; int square(int i)
square ;armcc把不縮排的正文作為乙個標號定義
mul r1,r0,r0
mov r0,r1 ;arm乘法指令有乙個限制,就是目標暫存器不能和第乙個引數暫存器相同
mov pc,lr ;對thumb指令,應該改為bx lr
end使用import,可以宣告其他檔案中定義的標號,要用arm c庫的話,就import |lib$request$armlib|, weak表示本行的標號如果找不到,不會報告連線錯誤。如果程式包含主程式main,那麼要引入標號__main,代表c庫初始化的開始。rn可以讓使用者給暫存器命名。
#eg:
area |.text|, code,readonly
export main
import |lib$$request$$armlib|, weak
import __main ;c library entry
import printf ;prints to stdout
i rn 4
;int main(void)
main
stmfd sp!,
mov i,#0
loop
adr r0,print_string
mov r1,i
mul r2,i,i
bl printf
add i,i,#1
cmp i,#10
blt loop
ldmfd sp!,
print_string
dcb " square of %d is %d/n", 0
endmap(別名^)和field(別名#),可以在堆疊中為變數和陣列定義和分配空間。
map 0 ;map symbols to offsets starting at offset 0?
a field 4 ;a is 4 bytes integer(at offset 0)
b field 2 ;b is bytes integer(at offset 4)
c field 64 ;c is an array of 64 characters (at offset 6)
length field 0 ;length records the current offset reached?
macro用來宣告巨集:
macro
checksum $ alignment
checksum_$ alignment
ldr w,[data],#4
l0 ;loop
if $ alignment<>0
add sum,sum,w,lsr #8, $alignement
ldr w,[data],#4
subs n,n,#1
add sum,sum,w,lsl#32-8* $ alignment
else
add sum,sum,w
ldr w,[data],#4
subs n,n,#1
endif
bgt %bt l0
mov pc,lr
mend
針對彙編的優化主要是面向硬體的。首先是流水線,有些load指令要需要多個週期來完成,可以通過調整指令的順序(當然要保證邏輯)來改善效能。另外,盡量讓程式只是用暫存器,方法是搞清楚資料占用暫存器的時間關係,實現暫存器有效的分時復用。另外,可以將長度較小的變數合併到乙個32位暫存器中儲存,以節省暫存器。由於pc可以通過程式操作,對於條件指令,可以直接用pc與形成分支的引數作運算來尋找對應的分支:
;int switch_relative(int x)
switch_relative
cmp x,#8
addlt pc,pc,x,lsl,#2
b method_d ;利用流水線,如果pc還是按順序那麼default分支的預取址就不
;會被沖掉
b method_0
b method_1
arm上的c程式設計
1.arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同
2.區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.
例如: 變數i,操作i++ ,如果int i, 則只需add r1,r1,#1 如果char i,則變成add r1,r1,#1
and r1,r1, 0xff .多了一條指令
3.迴圈最好用do{}while()型的,相比for(;;)型迴圈每次迴圈可以節省3條指令
4.函式引數也最好用int 型的,例如 short add(short x,short y)
編譯器為了保證輸入引數的是short型的會新增額外的指令,比如確保x是short型的,需要
mov r0,r0,lsr #16 mov r0,r0,asr #16
5.函式引數最好不要超過4個,因為前4個引數是通過暫存器r0-r3傳遞的,超過4個後的引數使用堆疊傳遞,速度慢多了.
6.適當的展開迴圈.迴圈有一定的開銷,在乙個迴圈中多做幾遍操作,減少迴圈的次數可以減少迴圈的開銷.
例如: i = 0; int i;
do dowhile(i<64) 改為 i++;}while(i<64)
當然,這樣做也增加了**長度.
7.使用減計數到0的迴圈結構,這樣就不用用暫存器儲存終止值.
8.使用無符號的迴圈計數值,迴圈條件是i!=0,而不是i>0, 這樣迴圈的開銷只有2條指令
arm上的彙編優化小方法
1. 加減法,邏輯操作佔乙個週期,目的位址是pc暫存器時增加乙個週期。分支指令佔3個週期。在cache命中的情況下,16位和8位的裝載指令(ldrh、ldrh等)佔乙個週期,但緊跟的2個週期不能使用裝入的資料。32位裝載指令佔乙個週期,緊跟的乙個週期不能使用裝載資料。如果裝載入pc,同樣要增加2個週期。
ldr r1,[r2] add r1,r1,r3 add r4,r4,r5 佔4個週期
改變次序後
ldr r1,[r2] add r4,r4,r5 add r1,r1,r3 佔3個週期
2. load指令佔時間比較長,在迴圈中可以使用預載的方法將load與跳轉指令放在一起,減少流水線的斷流。
例如:loop
ldrb r2,[r1]
............... //do
b loop
更改為ldrb r2,[r1]
loop
.............. //do
ldrb r2,[r1]
b loop
3. 迴圈展開時,可以在計算i步時就載入i+1步的資料,在i步的結果還沒準備好時執行i+1步計算。
4. arm只有16個可見暫存器,其中14個通用暫存器,1個堆疊指標r13,1個程式計數器r15。在影象處理的應用中很多是8位的運算元,可以利用32為暫存器一次進行兩組運算。
例如:加操作 100 + 50 和 2 + 3
位 24 16 8 0
運算元1 0 100 0 2
運算元2 0 50 0 3
結果 0 150 0 5
5. 暫存器數量不夠時,可用32為暫存器儲存兩個16位變數和4個8位變數。
ARM上的C程式設計
1 arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同 2 區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.例如 變數i,操作i 如果int i,則只需add r1,r1,1 如果char i,則變成add r1,r1,1 and r1...
ARM上的C程式設計
1 arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同 2 區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.例如 變數i,操作i 如果int i,則只需add r1,r1,1 如果char i,則變成add r1,r1,1 and r1...
ARM程式設計 關於ARM暫存器的巨集定義
arm程式設計 關於arm暫存器的巨集定義 gf86530430 136次 2008 8 27 請問各位大蝦 在arm的標頭檔案裡,對與暫存器的定義為什麼不跟51的一樣?如下 define rutxh0 volatile unsigned char 0x1d00020 define rutxh1 v...