當前位置:才華齋>IT認證>嵌入式>

嵌入式系統程式設計中的程式碼優化

嵌入式 閱讀(2.81W)

今天,嵌入式系統已經廣泛地應用於工業控制、資訊家電、辦公自動化、行動通訊、儀器儀表、醫療電子以及國防等領域。隨著國內外各種嵌入式產品的進一步開發和推廣,嵌入式技術越來越和人們的生活緊密結合。那麼嵌入式系統程式設計中的程式碼如何優化,下面跟yjbys小編一起來學習一下。

嵌入式系統程式設計中的程式碼優化

  1 引言

嵌入式系統一般指非PC系統,通常完成一種或多種特定的計算機功能。它是以應用為中心,軟硬體可裁減的,適應應用系統對功能,可靠性,成本,體積,功耗等綜合性要求的專用計算機系統。簡單的說類似於PC中的BIOS的工作方式,具有軟體程式碼小、高度自動化、響應速度快等特點。特別適合於要求實時和多工的應用體系。嵌入式實時系統是目前蓬勃發展的行業之一。但是,實時嵌入式系統的特點使得其軟體受時間和空間的嚴格限制,加上執行環境複雜,使得嵌入式系統軟體的開發變得異常困難。 為了設計一個滿足功能、效能和時間要求的安全可靠的高效能嵌入式系統,程式語言的選擇十分重要。

  2 嵌入式系統中程式語言的選擇

因為組合語言編寫的程式碼難懂,從而不好維護和難於除錯,且只能針對特定的體系結構和處理器移植性差,所以既不宜在複雜系統中使用,又不便於實現軟體重用;而高階語言具有良好的通用性和豐富的軟體支援,可移植性好、易於維護,因此高階語言程式設計具有許多優勢。隨著嵌入式系統應用範圍的不斷擴大和嵌入式實時作業系統RTOS(Real Time Operating System)的廣泛使用,高階語言程式設計已是嵌入式系統設計的必然趨勢。但是不排除一些軟體模組仍用匯編語言來寫,這可以使程式更加有效。雖然C/C++編譯器對程式碼進行了優化,但是適當的使用內聯彙編指令可以有效的提高整個系統執行的效率。目前,在嵌入式系統開發過程中使用的語言種類很多,但僅有少數幾種語言得到了比較廣泛的應用。其中C和C++是應用最廣泛的。C++在支援現代軟體工程、 OOP(Object Oriented Programming,面向物件的程式設計)、結構化等方面對C進行了卓有成效的改進,但在程式程式碼容量、執行速度、程式複雜程度等方面比C語言程式效能差一些。由於C語言既有低階語言的直接控制硬體的能力,又有高階語言的靈活性,是目前在嵌入式系統中應用最廣泛的程式語言。隨著網路技術和嵌入式技術的不斷髮展,Java的應用也得到廣泛應用。

  3 實時程式設計中程式碼的優化

在嵌入式的系統開發中,出於對低價產品的需求, 硬體的設計者需要提供剛好足夠的儲存器和完成工作的處理能力。所以在嵌入式軟體設計的最後一個階段則變成了對程式碼的優化。

程式碼優化的目標是體積小和速度快,可以從演算法、資料和指令流三方面來考慮。

3.1 演算法優化

大多數情況下,速度同記憶體(或者是效能,比如說壓縮效能)是不可兼得的。目前程式加速的常用演算法一個大方面就是利用查表來避免計算(比如在jpg有 huffman碼錶,在YUV到RGB變換也有變換表)這樣原來的複雜計算現在僅僅查表就可以了,雖然浪費了記憶體,不過速度顯著提升。此外在編寫程式時還要注意提高效率,例如:

3.1.1Switch語句中根據發生頻率來進行case排序

switch語句是一個普通的程式設計技術,編譯器會產生if-else-if的巢狀程式碼,並按照順序進行比較,發現匹配時,就跳轉到滿足條件的語句執行。使用時需要注意。每一個由機器語言實現的測試和跳轉僅僅是為了決定下一步要做什麼,就把寶貴的處理器時間耗盡。為了提高速度,設法根據具體的情況按照它們發生的相對頻率排序。換句話說,把最可能發生的情況放在第一位,最不可能的情況放在最後。

3.1.2將大的switch語句轉為巢狀switch語句

當switch語句中的case標號很多時,為了減少比較的次數,明智的做法是把大switch語句轉為巢狀switch語句。把發生頻率高的case 標號放在一個switch語句中,並且是巢狀switch語句的最外層,發生相對頻率相對低的case標號放在另一個switch語句中。如果switch中每一種情況下都有很多的工作要做,那麼把整個switch語句用一個指向函式指標的表來替換會更加有效。

3.1.3用指標代替陣列

在許多種情況下,可以用指標運算代替陣列索引,這樣做常常能產生又快又短的程式碼。與陣列索引相比,指標一般能使程式碼速度更快,佔用空間更少。使用多維陣列時差異更明顯。下面的程式碼作用是相同的,但是效率不一樣。

陣列索引 指標運算

For(;;){ p=array

A=array[r++]; for(;;){

a=*(p++);

...... ......

} }

指標方法的優點是,array的地址每次裝入地址p後,在每次迴圈中只需對p增量操作。在陣列索引方法中,每次迴圈中都必須進行基於r值求陣列下標的複雜運算。

3.1.4使用巨集函式而不是函式。例如:

#define bwMCDR2_ADDRESS 4

#define bsMCDR2_ADDRESS 17

#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)

#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))

#define SET_BITS(__dst, __bf, __val) ((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

函式和巨集函式的區別就在於,巨集函式佔用了大量的空間,而函式佔用了時間。函式呼叫是要使用系統的棧來儲存資料的,如果編譯器裡有棧檢查選項,一般在函式的頭會嵌入一些彙編語句對當前棧進行檢查;同時,CPU也要在函式呼叫時儲存和恢復當前的現場,進行壓棧和彈棧操作,所以,函式呼叫需要一些CPU時間。而巨集函式不存在這個問題。巨集函式僅僅作為預先寫好的程式碼嵌入到當前程式,不會產生函式呼叫,所以僅僅是佔用了空間,在頻繁呼叫同一個巨集函式的時候,該現象尤其突出。

3.2 Data optimization資料優化

比演算法優化層低一級的是資料優化層,我們可以通過改變演算法使用的資料型別來優化演算法。主要的目的是使處理的資料和目標結構的特性相一致。這項優化不需要大量的程式碼重寫,並獨立於演算法優化的執行而執行.例如:

3.2.1確定浮點型變數和表示式是 float 型

為了讓編譯器產生更好的程式碼,必須確定浮點型變數和表示式是 float 型的。要特別注意的是,以 ";F"; 或 ";f";為字尾(比如:2.718f)的浮點常量才是 float 型,否則預設是 double 型。為了避免 float 型引數自動轉化為 double,請在函式宣告時使用 float。

3.2.2使用32位的資料型別

編譯器有很多種,但它們都包含的典型的32位型別是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。儘量使用32位的資料型別,因為它們比16位的資料甚至8位的資料更有效率。

3.2.3明智使用有符號整型變數

在很多情況下,你需要考慮整型變數是有符號還是無符號型別的'。在許多地方,考慮是否使用有符號的變數是必要的。在一些情況下,有符號的運算比較快;但在一些情況下卻相反。比如:整型到浮點轉化時,使用大於16位的有符號整型比較快。因為x86構架中提供了從有符號整型轉化到浮點型的指令,但沒有提供從無符號整型轉化到浮點的指令。在整數運算中計算商和餘數時,使用無符號型別比較快。

3.3 Instruction flow optimization指令流優化

第三層優化的目標是低階指令流。比較常見的技術是迴圈合併(loop merging),迴圈展開(unrolling),軟體流水(software pipelining)。

3.3.1迴圈合併

如果兩個迴圈計數差不多、迴圈執行互不相同的操作,可以把它們合併在一起組成一個迴圈。當兩個迴圈的負荷都不滿時,這是非常有用的。

3.3.2迴圈展開

迴圈展開就是把迴圈計數小的迴圈展開,成為非迴圈形式的序列程式,或者把迴圈計數大的迴圈部分展開,減少迴圈迭代次數,這樣可以節省了用於迴圈設定、初始化、增加和校對迴圈計數器的時間。大多數編譯器可以自動完成這項工作,手工編譯會出現錯例如:

for( int i = 0; i < 3; i++ ) array[i] = i;

邏輯上等同於:

array[0] = 0; array[1] = 1, array[2] = 2;

3.3.3軟體流水

軟體流水是用來安排迴圈指令,使這個迴圈多次迭代並行執行的一種技術。在巢狀迴圈中,編譯器僅對最裡面的迴圈執行軟體流水,因此對執行週期很少的內迴圈作迴圈展開,外迴圈進行軟體流水,這樣可以改進C程式碼並行執行的效能。使用軟體流水還應當注意:儘管軟體流水迴圈可以包含行內函數,但是不能包含函式呼叫;在迴圈中不可以有條件終止指令;在迴圈體中不可以修改迴圈控制變數。

  4 總結語

現代的C和C++編譯器都提供了一定程度上的程式碼優化。然而,大部分由編譯器執行的優化僅涉及執行速度和程式碼大小的一個平衡。你的程式能夠變得更快或者更小,但是不可能又變快又變小。上面介紹的方法主要是為了提高程式碼的效率。但是事實上,在使用這些技術提高程式碼執行速度的同時會相應的產生一些負面的影響,比如增加程式碼的大小、降低程式可讀性等。不過你可以讓C/C++編譯器來進行減少程式碼大小的優化,而手動利用程式設計來減少程式碼的執行時間。在嵌入式程式設計中合理地使用這幾種技術有時會達到很好 的優化效果。

  參考文獻:

[1]王春寧.嵌入式系統程式設計原始碼解析[M].北京:電子工業出版社,2002.

[2]王田苗.嵌入式系統設計與例項開發[M].北京:清華大學出版社,2002.

[3] Michael Barr.譯者:於志巨集 C/C++嵌入式系統程式設計 [M]. 北京:中國電力出版社,2001.

[4] Tajana ˇSimuni′c, Luca Benini , Giovanni De Micheli and Mat Hans. Source Code Optimization and Profiling of Energy Consumption in Embedded Systems [J ]. In Proceedings of IEEE International Symposium 誤程式碼。