1 引言
計算機技術和信息技術的高速發展的今天,計算機和計算機技術大量應用在人們的日常生活中,嵌入式計算機也得到了廣泛的應用。 嵌入式計算機是指完成一種或多種特定功能的計算機系統,是軟硬件的緊密結合體。具有軟件代碼小、高度自動化、響應速度快等特點。 特別適合於要求實時和多任務的應用體系。嵌入式實時系統是目前蓬勃發展的行業之一。 但是,實時嵌入式系統的特點使得其軟件受時間和空間的嚴格限制,加上運行環境復雜,使得嵌入式系統軟件的開發變得異常困難。 為了設計一個滿足功能、性能和死線要求的系統,為了開發出安全可靠的高性能嵌入式系統,開發語言的選擇十分重要。
2 嵌入式實時程序設計中語言的選擇
隨著嵌入式系統應用范圍的不斷擴大和嵌入式實時操作系統RTOS(Real Time Operating System)的廣泛使用,高級語言編程已是嵌入式系統設計的必然趨勢。因為匯編語言和具體的微處理器的硬件結構密切相關,移植性較差,既不宜在復雜系統中使用,又不便於實現軟件重用;而高級語言具有良好的通用性和豐富的軟件支持,便於推廣、易於維護,因此高級語言編程具有許多優勢。目前,在嵌入式系統開發過程中使用的語言種類很多,但僅有少數幾種語言得到了比較廣泛的應用。其中C和C++是應用最廣泛的。C++在支持現代軟件工程、 OOP(Object Oriented Programming,面向對象的程序設計)、結構化等方面對C進行了卓有成效的改進,但在程序代碼容量、執行速度、程序復雜程度等方面比C語言程序性能差一些。由於C語言既有低級語言的直接控制硬件的能力,又有高級語言的靈活性,是目前在嵌入式系統中應用最廣泛的編程語言。隨著網絡技術和嵌入式技術的不斷發展,Java的應用也得到廣泛應用。
3 C/C++代碼在實時程序設計中的優化
雖然使軟件正確是一個工程合乎邏輯的最後一個步驟,但是在嵌入式的系統開發中,情況並不總是這樣的。出於對低價產品的需求, 硬件的設計者需要提供剛好足夠的存儲器和完成工作的處理能力。所以在嵌入式軟件設計的最後一個階段則變成了對代碼的優化。
現代的C和C++編譯器都提供了一定程度上的代碼優化。然而,大部分由編譯器執行的優化僅涉及執行速度和代碼大小的一個平衡。你的程序能夠變得更快或者更小,但是不可能又變快又變小。經過本人在嵌入式系統設計和實現過程中實踐,下面介紹幾種簡單且行之有效的C/C++代碼的優化方法。
(1) Inline函數
在C++中,關鍵字Inline可以被加入到任何函數的聲明中。這個關鍵字請求編譯器用函數內部的代碼替換所有對於指出的函數的調用。這樣做在兩個方面快於函數調用。這樣做在兩個方面快於函數調用:第一,省去了調用指令需要的執行時間;第二,省去了傳遞變元和傳遞過程需要的時間。但是使用這種方法在優化程序速度的同時,程序長度變大了,因此需要更多的ROM。使用這種優化在Inline函數頻繁調用並且只包含幾行代碼的時候是最有效的。
(2)用指針代替數組
在許多種情況下,可以用指針運算代替數組索引,這樣做常常能產生又快又短的代碼。與數組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數組時差異更明顯。下面的代碼作用是相同的,但是效率不一樣。
數組索引 指針運算
For(;;){ p=array
A=array[t++]; for(;;){
a=*(p++);
...... ......
} }
指針方法的優點是,array的地址每次裝入地址p後,在每次循環中只需對p增量操作。在數組索引方法中,每次循環中都必須進行基於t值求數組下標的復雜運算。
(3)不定義不使用的返回值
function函數定義並不知道函數返回值是否被使用,假如返回值從來不會被用到,應該使用void來明確聲明函數不返回任何值。
(4)手動編寫匯編
在嵌入式軟件開發中,一些軟件模塊最好用匯編語言來寫,這可以使程序更加有效。雖然C/C++編譯器對代碼進行了優化,但是適當的使用內聯匯編指令可以有效的提高整個系統運行的效率。
(5)使用寄存器變量
在聲明局部變量的時候可以使用register關鍵字。這就使得編譯器把變量放入一個多用途的寄存器中,而不是在堆棧中,合理使用這種方法可以提高執行速度。函數調用越是頻繁,越是可能提高代碼的速度。
(6)使用增量和減量操作符
在使用到加一和減一操作時盡量使用增量和減量操作符,因為增量符語句比賦值語句更快,原因在於對大多數CPU來說,對內存字的增、 減量操作不必明顯地使用取內存和寫內存的指令,比如下面這條語句:
x=x+1;
模仿大多數微機匯編語言為例,產生的代碼類似於:
move A,x ;把x從內存取出存入累加器A
add A,1 ;累加器A加1
store x ;把新值存回x
如果使用增量操作符,生成的代碼如下:
incr x ;x加1
顯然,不用取指令和存指令,增、減量操作執行的速度加快,同時長度也縮短了。
(7)減少函數調用參數
使用全局變量比函數傳遞參數更加有效率。這樣做去除了函數調用參數入棧和函數完成後參數出棧所需要的時間。然而決定使用全局變量會影響程序的模塊化和重入,故要慎重使用。
(8)Switch語句中根據發生頻率來進行case排序
switch語句是一個普通的編程技術,編譯器會產生if-else-if的嵌套代碼,並按照順序進行比較,發現匹配時,就跳轉到滿足條件的語句執行。使用時需要注意。每一個由機器語言實現的測試和跳轉僅僅是為了決定下一步要做什麼,就把寶貴的處理器時間耗盡。為了提高速度,沒法把具體的情況按照它們發生的相對頻率排序。換句話說,把最可能發生的情況放在第一位,最不可能的情況放在最後。
(9)將大的switch語句轉為嵌套switch語句
當switch語句中的case標號很多時,為了減少比較的次數,明智的做法是把大switch語句轉為嵌套switch語句。把發生頻率高的case 標號放在一個switch語句中,並且是嵌套switch語句的最外層,發生相對頻率相對低的case標號放在另一個switch語句中。比如,下面的程序段把相對發生頻率低的情況放在缺省的case標號內。
pMsg=ReceiveMessage();
switch (pMsg->type)
{
case FREQUENT_MSG1:
handleFrequentMsg();
break;
case FREQUENT_MSG2:
handleFrequentMsg2();
break;
......
case FREQUENT_MSGn:
handleFrequentMsgn();
break;
default: //嵌套部分用來處理不經常發生的消息
switch (pMsg->type)
{
case INFREQUENT_MSG1:
handleInfrequentMsg1();
break;
case INFREQUENT_MSG2:
handleInfrequentMsg2();
break;
......
case INFREQUENT_MSGm:
handleInfrequentMsgm();
break;
}
}
如果switch中每一種情況下都有很多的工作要做,那麼把整個switch語句用一個指向函數指針的表來替換會更加有效,比如下面的switch語句,有三種情況:
enum MsgType{Msg1, Msg2, Msg3}
switch (ReceiveMessage()
{
case Msg1;
......
case Msg2;
.....
case Msg3;
.....
}
為了提高執行速度,用下面這段代碼來替換這個上面的switch語句。
/*准備工作*/
int handleMsg1(void);
int handleMsg2(void);
int handleMsg3(void);
/*創建一個函數指針數組*/
int (*MsgFunction [])()={handleMsg1, handleMsg2, handleMsg3};
/*用下面這行更有效的代碼來替換switch語句*/
status=MsgFunction[ReceiveMessage()]();
(10)避免使用C++的昂貴特性
C++在支持現代軟件工程、OOP、結構化等方面對C進行了卓有成效的改進,但在程序代碼容量、執行速度、程序復雜程度等方面比C語言程序性能差一些。並不是所有的C++特性都是骯貴的。比如,類的定義是完全有益的。公有和私有成員數據及函數的列表與一個 struct 及函數原形的列表並沒有多大的差別。單純的加入類既不會影響代碼的大小,也不會影響程序的效率。但C++的多重繼承、虛擬基類、模板、異常處理及運行類型識別等特性對代碼的大小和效率有負面的影響,因此對於C++的一些特性要慎重使用,可做些實驗看看它們對應用程序的影響。
4 總結語
在嵌入式實時程序設計時可以運用上面介紹的一種或多種技術來優化代碼。上面介紹的方法主要是為了提高代碼的效率。但是事實上,在使用這些技術提高代碼運行速度的同時會相應的產生一些負面的影響,比如增加代碼的大小、降低程序可讀性等。不過你可以讓C/C++編譯器來進行減少代碼大小的優化,而手動利用以上技術來減少代碼的執行時間。在嵌入式程序設計中合理地使用這幾種技術有時會達到很好 的優化效果。