手機(jī)版WPS 手機(jī)版wps免費下載
2022-11-29
更新時間:2022-04-03 16:11:09作者:未知
在性能優(yōu)化方面永遠(yuǎn)注意80-20原則,即20%的程序消耗了80%的運行時間,因而我們要改進(jìn)效率,最主要是考慮改進(jìn)那20%的代碼。不要優(yōu)化程序中開銷不大的那80%,這是勞而無功的。
第一招:以空間換時間
計算機(jī)程序中最大的矛盾是空間和時間的矛盾,那么,從這個角度出發(fā)逆向思維來考慮程序的效率問題,我們就有了解決問題的第1招--以空間換時間。比如說字符串的賦值:
方法A:通常的辦法
#define LEN 32
char string1 [LEN];
memset (string1,0,LEN);
strcpy (string1,"This is a example!!");
方法B:
const char string2[LEN] ="This is a example!";
char * cp;
cp = string2 ;
使用的時候可以直接用指針來操作。
從上面的例子可以看出,A和B的效率是不能比的。在同樣的存儲空間下,B直接使用指針就可以操作了,而A需要調(diào)用兩個字符函數(shù)才能完成。B的缺點在于靈活性沒有A好。在需要頻繁更改一個字符串內(nèi)容的時候,A具有更好的靈活性;如果采用方法B,則需要預(yù)存許多字符串,雖然占用了大量的內(nèi)存,但是獲得了程序執(zhí)行的高效率。
如果系統(tǒng)的實時性要求很高,內(nèi)存還有一些,那我推薦你使用該招數(shù)。
第二招: 使用宏而不是函數(shù)。
這也是第一招的變招。函數(shù)和宏的區(qū)別就在于,宏占用了大量的空間,而函數(shù)占用了時間。大家要知道的是,函數(shù)調(diào)用是要使用系統(tǒng)的棧來保存數(shù)據(jù)的,如果編譯器里有棧檢查選 項,一般在函數(shù)的頭會嵌入一些匯編語句對當(dāng)前棧進(jìn)行檢查;同時,CPU也要在函數(shù)調(diào)用時保存和恢復(fù)當(dāng)前的現(xiàn)場,進(jìn)行壓棧和彈棧操作,所以,函數(shù)調(diào)用需要一 些CPU時間。 而宏不存在這個問題。宏僅僅作為預(yù)先寫好的代碼嵌入到當(dāng)前程序,不會產(chǎn)生函數(shù)調(diào)用,所以僅僅是占用了空間,在頻繁調(diào)用同一個宏的時候,該現(xiàn)象尤其突出。
舉例如下:
方法C:
#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
int BIT_MASK(int __bf)
{
return ((1U << (bw ## __bf)) - 1)<< (bs ## __bf);
}
void SET_BITS(int __dst,
int __bf, int __val)
{
__dst = ((__dst) & ~(BIT_MASK(__bf))) |
\
(((__val) << (bs ## __bf))
& (BIT_MASK(__bf))))
}
SET_BITS(MCDR2, MCDR2_ADDRESS,ReGISterNumber);
方法D:
#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);
D方法是我看到的最好的置位操作函數(shù),是arm公司源碼的一部分,在短短的三行內(nèi)實現(xiàn)了很多功能,幾乎涵蓋了所有的位操作功能。C方法是其變體,其中滋味還需大家仔細(xì)體會。
第三招:數(shù)學(xué)方法解決問題
現(xiàn)在我們演繹高效C語言編寫的第二招--采用數(shù)學(xué)方法來解決問題。數(shù)學(xué)是計算機(jī)之母,沒有數(shù)學(xué)的依據(jù)和基礎(chǔ),就沒有計算機(jī)的發(fā)展,所以在編寫程序的時候,采用一些數(shù)學(xué)方法會對程序的執(zhí)行效率有數(shù)量級的提高。舉例如下,求 1~100的和。
方法E:
int I , j;
for (I = 1 ;I<=100; I ++)
{
j += I;
}
方法F
int I;
I = (100 * (1+100)) / 2
這個例子是我印象最深的一個數(shù)學(xué)用例,是我的計算機(jī)啟蒙老師考我的。當(dāng)時我只有小學(xué)三年級,可惜我當(dāng)時不知道用公式 N×(N+1)/ 2 來解決這個問題。方法E循環(huán)了100次才解決問題,也就是說最少用了100個賦值,100個判斷,200個加法(I和j);而方法F僅僅用了1個加法,1 次乘法,1次除法。效果自然不言而喻。所以,現(xiàn)在我在編程序的時候,更多的是動腦筋找規(guī)律,最大限度地發(fā)揮數(shù)學(xué)的威力來提高程序運行的效率。
第四招:使用位操作
使用位操作。減少除法和取模的運算。在計算機(jī)程序中數(shù)據(jù)的位是可以操作的最小數(shù)據(jù)單位,理論上可以用"位運算"來完成所有的運算和操作。一般的位操作是用來控制硬件的,或者做數(shù)據(jù)變換使用,但是,靈活的位操作可以有效地提高程序運行的效率。舉例如下:
方法G
int I,J;
I = 257 /8;
J = 456 % 32;
方法H
int I,J;
I = 257 >>3;
J = 456 - (456 >> 4 << 4);
在字面上好像H比G麻煩了好多,但是,仔細(xì)查看產(chǎn)生的匯編代碼就會明白,方法G調(diào)用了基本的取模函數(shù)和除法函數(shù),既有函數(shù)調(diào)用,還有很多匯編代碼和寄存器參與運算;而方法H則僅僅是幾句相關(guān)的匯編,代碼更簡潔,效率更高。當(dāng)然,由于編譯器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,arm C 來看,效率的差距還是不小。
對于以2的指數(shù)次方為"*"、"/"或"%"因子的數(shù)學(xué)運算,轉(zhuǎn)化為移位運算"<< >>"通??梢蕴岣咚惴ㄐ?。因為乘除運算指令周期通常比移位運算大。
C語言位運算除了可以提高運算效率外,在嵌入式系統(tǒng)的編程中,它的另一個最典型的應(yīng)用,而且十分廣泛地正在被使用著的是位間的與(&)、或(|)、非(~)操作,這跟嵌入式系統(tǒng)的編程特點有很大關(guān)系。我們通常要對硬件寄存器進(jìn)行位設(shè)置,譬如,我們通過將AM186ER型80186處理器的中斷屏蔽控制寄存器的第低6位設(shè)置為0(開中斷2),最通用的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp &~INT_I2_MASK);
而將該位設(shè)置為1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp | INT_I2_MASK);
判斷該位是否為1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
if(wTemp & INT_I2_MASK)
{
… /* 該位為1 */
}
運用這招需要注意的是,因為CPU的不同而產(chǎn)生的問題。比如說,在PC上用這招編寫的程序,并在PC上調(diào)試通過,在移植到一個16位機(jī)平臺上的時候,可能會產(chǎn)生代碼隱患。所以只有在一定技術(shù)進(jìn)階的基礎(chǔ)下才可以使用這招。
第五招:匯編嵌入
在熟悉匯編語言的人眼里,C語言編寫的程序都是垃圾"。這種說法雖然偏激了一些,但是卻有它的道理。匯編語言是效率最高的計算機(jī)語言,但是,不可能靠著它來寫一個操作系統(tǒng)吧?所以,為了獲得程序的高效率,我們只好采用變通的方法--嵌入?yún)R編,混合編程。嵌入式C程序中主要使用在線匯編,即在C程序中直接插入_asm{ }內(nèi)嵌匯編語句。
舉例如下,將數(shù)組一賦值給數(shù)組二,要求每一字節(jié)都相符。
char string1[1024],string2[1024];
方法I
int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I)
方法J
#ifdef _PC_
int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I);
#else
#ifdef _arm_
__asm
{
MOV R0,string1
MOV R1,string2
MOV R2,#0
loop:
LDMIA R0!, [R3-R11]
STMIA R1!, [R3-R11]
ADD R2,R2,#8
CMP R2, #400
BNE loop
}
#endif
再舉個例子:
/* 把兩個輸入?yún)?shù)的值相加,結(jié)果存放到另外一個全局變量中 */
int result;
void Add(long a, long *b)
{
_asm
{
MOV AX, a
MOV BX, b
ADD AX, [BX]
MOV result, AX
}
}
方法I是最常見的方法,使用了1024次循環(huán);方法J則根據(jù)平臺不同做了區(qū)分,在arm平臺下,用嵌入?yún)R編僅用128次循環(huán)就完成了同樣的操作。這里有朋友會說,為什么不用標(biāo)準(zhǔn)的內(nèi)存拷貝函數(shù)呢?這是因為在源數(shù)據(jù)里可能含有數(shù)據(jù)為0的字節(jié),這樣的話,標(biāo)準(zhǔn)庫函數(shù)會提前結(jié)束而不會完成我們要求的操作。這個例程典型應(yīng)用于LCD數(shù)據(jù)的拷貝過程。根據(jù)不同的CPU,熟練使用相應(yīng)的嵌入?yún)R編,可以大大提高程序執(zhí)行的效率。
雖然是必殺技,但是如果輕易使用會付出慘重的代價。這是因為,使用了嵌入?yún)R編,便限制了程序的可移植性,使程序在不同平臺移植的過程中,臥虎藏龍,險象環(huán)生!同時該招數(shù)也與現(xiàn)代軟件工程的思想相違背,只有在迫不得已的情況下才可以采用。
第六招, 使用寄存器變量
當(dāng)對一個變量頻繁被讀寫時,需要反復(fù)訪問內(nèi)存,從而花費大量的存取時間。為此,C語言提供了一種變量,即寄存器變量。這種變量存放在CPU的寄存器中,使用時,不需要訪問內(nèi)存,而直接從寄存器中讀寫,從而提高效率。寄存器變量的說明符是register。對于循環(huán)次數(shù)較多的循環(huán)控制變量及循環(huán)體內(nèi)反復(fù)使用的變量均可定義為寄存器變量,而循環(huán)計數(shù)是應(yīng)用寄存器變量的最好候選者。
(1) 只有局部自動變量和形參才可以定義為寄存器變量。因為寄存器變量屬于動態(tài)存儲方式,凡需要采用靜態(tài)存儲方式的量都不能定義為寄存器變量,包括:模塊間全局變量、模塊內(nèi)全局變量、局部static變量;
(2) register是一個"建議"型關(guān)鍵字,意指程序建議該變量放在寄存器中,但最終該變量可能因為條件不滿足并未成為寄存器變量,而是被放在了存儲器中,但編譯器中并不報錯(在C++語言中有另一個"建議"型關(guān)鍵字:inline)。
下面是一個采用寄存器變量的例子:
/* 求1+2+3+….+n的值 */
WORD Addition(BYTE n)
{
register i,s=0;
for(i=1;i<=n;i++)
{
s=s+i;
}
return s;
}
本程序循環(huán)n次,i和s都被頻繁使用,因此可定義為寄存器變量。
/*www.cyuyan.com.cn*/
第七招: 利用硬件特性
首先要明白CPU對各種存儲器的訪問速度,基本上是:
CPU內(nèi)部RAM > 外部同步RAM > 外部異步RAM > FLASH/ROM
對于程序代碼,已經(jīng)被燒錄在FLASH或ROM中,我們可以讓CPU直接從其中讀取代碼執(zhí)行,但通常這不是一個好辦法,我們最好在系統(tǒng)啟動后將FLASH或ROM中的目標(biāo)代碼拷貝入RAM中后再執(zhí)行以提高取指令速度;
對于UART等設(shè)備,其內(nèi)部有一定容量的接收BUFFER,我們應(yīng)盡量在BUFFER被占滿后再向CPU提出中斷。例如計算機(jī)終端在向目標(biāo)機(jī)通過RS-232傳遞數(shù)據(jù)時,不宜設(shè)置UART只接收到一個BYTE就向CPU提中斷,從而無謂浪費中斷處理時間;
如果對某設(shè)備能采取DMA方式讀取,就采用DMA讀取,DMA讀取方式在讀取目標(biāo)中包含的存儲信息較大時效率較高,其數(shù)據(jù)傳輸?shù)幕締挝皇菈K,而所傳輸?shù)臄?shù)據(jù)是從設(shè)備直接送入內(nèi)存的(或者相反)。DMA方式較之中斷驅(qū)動方式,減少了CPU 對外設(shè)的干預(yù),進(jìn)一步提高了CPU與外設(shè)的并行操作程度。