基於ARM的高效C語言程式設計

2021-07-16 12:30:51 字數 3141 閱讀 6624

arm處理器提高執行速度和減小**尺寸是嵌入式軟體設計的關鍵需求,以其高效能、低功耗、低成本等優勢被廣泛應用於各種成功的32位嵌入式系統中。儘管大多數的arm編譯器和偵錯程式都帶有效能優化工具,但是為了保證其正確性,編譯器必須是穩妥和安全的,而且它還受到處理器自身結構的限制。因此,程式設計人員必須在理解編譯器工作特點的基礎上來實現**優化。**的優化方法較多,本文針對函式優化方法進行闡述。

1  函式區域性變數的資料型別

區域性變數包括函式內區域性變數、函式引數、函式返回值。由於arm資料操作都是32位,即使資料本身只需要8位或16位,對於這三類區域性變數也應盡可能使用32位的資料型別int或long,以提高**執行效率。下面以簡單求和函式為例進行分析。    

函式add1計算包含10個字的陣列array的累加和,add2與add1功能相同,只是將函式add1的引數array型別改為16位的short,函式內區域性變數i型別改為8位的char,sum改為16位的short。add1、add2的c源**如下:

int add1(int *array)

short add2(short *array)

add1經編譯產生的彙編**:

add1

mov r2,r0

mov r0,#0

mov r1,#0

add1_loop

ldr r3,[r2,r1,lsl #2]

add r1,r1,#1

cmp r1,#0x0a

add r0,r3,r0

bcc add1_loop

mov pc,r14

add2經編譯產生的彙編**:

add2

mov r2,r0

mov r0,#0

mov r1,#0

add2_loop

add r3,[r2,r1,lsl #1];

增加語句

①ldrh r3,[r3,#0]

add r1,r1,#1

and r1,r1,0xff;增加語句

②cmp r1,#0x0a

add r0,r3,r0

bcc add2_loop

mov r0,r0,lsl #16;增加語句

③mov r0,r0,asr #16;增加語句

④mov pc,r14

比較add1和add2兩個函式的彙編**,可以發現add2_loop迴圈比add1_loop迴圈增加了4條語句。

語句①:函式add2中變數sum為16位short型別,arm指令中ldrh指令不支援移位位址偏移,因此增加add指令計算陣列下標位址。

語句②:由於函式add2中迴圈變數i為8位的char型別,而arm處理器的暫存器為32位,此語句用於處理迴圈變數累加過程中引起的溢位問題。即:當i累加到255時,再加1應該為0,而不是256。

語句③、④:函式add2中返回結果sum為short型別,在返回前需將32位暫存器的前16位用符號位填充,即轉換為16位short型別。

2  函式區域性變數的個數

為了加快程式的執行速度,函式編譯時應盡可能將區域性變數都分配在暫存器中。當區域性變數多於可用的暫存器時,編譯器會將多餘的變數壓入堆疊(即存入儲存器中),因此必須控制區域性變數的個數。

arm處理器採用risc結構,帶有豐富的內部暫存器。在編譯器使用apcs開關選項,即支援atpcs(armthumb procedure call standard)標準時,理論上有14個暫存器(r0~r12,r14)可以用來存放區域性變數。但是實際上有些暫存器有自身特殊的用途,例如r9在與讀 寫位置無關(rwpi)的編譯情況下作為靜態基址暫存器使用,r12作為子程式內部呼叫的臨時過渡暫存器使用。atpcs規則中的暫存器名稱及說明如表1 所列。

表1  atpcs規則中暫存器說明

[url=

因此,應盡量限制區域性變數的數目:①對於函式的引數個數應控制在4個以內,只有r0~r3可用來儲存引數,當引數多於4個時將被壓入堆疊。如果由於實際應用的需要,引數多於4個,也可以採用結構體來組織引數,傳遞結構體指標來實現。②函式內部區域性變數的個數應控制在12個以內 (r0~r11),r12~r15都有特定用途。

3  函式內**的編寫

3.1  迴圈**的編寫

迴圈的控制條件設為遞減到零的形式,可以減少指令條數。以求10個數的累加和為例進行分析。

**1:

int sum=0;

for(int i=0;i<10;i++)

sum=sum+i;

**2:

int sum=0;

for(int i=10;i!=0;i--)

sum=sum+i;

彙編**1:

mov r0,#0

mov r1,#0

add1

add r1,r1,#1

cmp r1,#0x0a

add r0,r1,r0

bcc add1

彙編**2:

mov r0,#0

mov r1,#0x0a

add2

subs r1,r1,#1

add r0,r1,r0

bne add2

比較**1和**2,兩者的功能是相同的,但是**2在迴圈中少了1條指令。該迴圈的執行次數為10次,即在執行時共減少了10條指令。

3.2  內聯函式的使用

當函式體**較少(通常只有一兩條語句),且又被經常呼叫時,可將它設為內聯函式(inline)。對內聯函式的呼叫類似於巨集定義的展開,因此沒有函式呼叫的開銷(即引數的傳遞和函式值的返回),只是增加了被呼叫函式的**量。

例如在嵌入式系統中,經常訪問的外設埠的讀寫**就可以設成內聯函式,以提高執行效率。外設暫存器的讀寫函式如下:

inline unsigned short reg_read(unsigned short reg)

inline void reg_write(unsigned short reg, unsigned short val)

這兩個函式的共同特點是:函式體的**很少,只有1個語句;使用的區域性變數很少,只有1~2個引數。由於定義為內聯函式,程式的可讀性較好;在執行時由於沒有呼叫開銷,執行效率較高;函式體很小,在被展開時空間開銷不大。

結語由於嵌入式系統對儲存空間的限制和實時性的需求,在編寫**時必須採用相應的方法和原則以減少**的空間開銷和時間開銷。**優化需要花費時間,並且**優化後將降低源**的可讀性。因此,只有對經常被呼叫且對效能影響較大的函式進行優化,才能最有效地優化系統。

如何在ARM下進行高效的C程式設計?

如何在arm下進行高效的c程式設計?1.對區域性變數 函式引數和返回值要使用signed和unsigned int型別。這樣可以避免型別轉換,而且可高效地使用arm的32位資料操作指令。2.最高效的迴圈體形式是減計數到零 counts down to zero 的do while迴圈。3.展開重要的...

ARM彙編 C語言 混合程式設計

主函式main 第一次實驗 12月4 這是我見過最坑爹的問題 main這個字段不能使用。在release模式下怎麼編譯都過不去,報錯如下 原因 不能使用main這個識別符號。用main main main1 mai都行,就是main不行。原來在debugrel模式下,使用main僅僅是乙個警告,沒想...

高效C 程式設計

推薦編寫c 的 風格,看似容易,堅持不易,且寫且珍惜!陳國林 1.版本和版本宣告 版本和版本檔案宣告位於標頭檔案和定義檔案的開頭,主要內容 1 版本資訊 2 檔名稱 識別符號 摘要 3 當前的版本號 作者 修改日期 4 版本歷史資訊 2.程式版式 1 在每個類宣告之後 每個函式定義之後都要加上乙個空...