嵌入式代碼優(yōu)化是一個復(fù)雜的過程,它不僅取決于代碼本身,還取決于目標(biāo)硬件平臺、編譯器以及優(yōu)化的目標(biāo)(例如速度、內(nèi)存使用、功耗等)。
不過,有一些通用的技巧可以在編寫嵌入式代碼時考慮到:
使用查表法
在內(nèi)存空間較為充足的情況下,有時候可以犧牲一些空間來換取程序的運行速度。查表法就是 以空間換取時間 的典型例子。
比如:編寫程序統(tǒng)計一個4bit(0x0~0xF)數(shù)據(jù)中1的個數(shù)。
使用查表法:
staticinttable[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; intget_digits_1_num(unsignedchardata) { intcnt=0; unsignedchartemp=data&0xf; cnt=table[temp]; returncnt; }
優(yōu)于:
intget_digits_1_num(unsignedchardata) { intcnt=0; unsignedchartemp=data&0xf; for(inti=0;i4;?i++) ?{ ??if?(temp?&?0x01) ??{ ???cnt++; ??} ??temp?>>=1; } returncnt; }
查表法把0x0~0xF中的所有數(shù)據(jù)中每個數(shù)據(jù)的1的個數(shù)都記錄下來,存放到一個表中。這樣一來,數(shù)據(jù)與數(shù)據(jù)中1的個數(shù)就建立起了一一對應(yīng)關(guān)系,就可以通過數(shù)組索引來獲取得到結(jié)果。常規(guī)法使用for循環(huán)的方式來實現(xiàn),缺點是占用了不少處理器的時間。
特別地,對于越復(fù)雜地運算,查表法較常規(guī)法更有優(yōu)勢。另一方面,查表法的代碼往往比常規(guī)法要簡潔些。
使用柔性數(shù)組
C99中,結(jié)構(gòu)體中的最后一個元素允許是未知大小的數(shù)組,這就叫作 柔性數(shù)組 。
柔性數(shù)組的特點:
結(jié)構(gòu)體中柔性數(shù)組成員前面必須至少有一個其他成員。
sizeof返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。
包含柔性數(shù)組成員的結(jié)構(gòu)用malloc()函數(shù)進行內(nèi)存的動態(tài)分配。
在C99標(biāo)準(zhǔn)環(huán)境中,使用柔性數(shù)組:
typedefstruct_protocol_format { uint16_thead; uint8_tid; uint8_ttype; uint8_tlength; uint8_tvalue[]; }protocol_format_t;
優(yōu)于使用指針:
typedefstruct_protocol_format { uint16_thead; uint8_tid; uint8_ttype; uint8_tlength; uint8_t*value; }protocol_format_t;
柔性數(shù)組的方式結(jié)構(gòu)體占用較指針的方式少。
柔性數(shù)組的方式相對與指針的方式更為簡潔,給結(jié)構(gòu)體申請空間的同時也給柔性數(shù)組申請空間,柔性數(shù)組的方式只需要申請一次空間,是一塊連續(xù)內(nèi)存,連續(xù)的內(nèi)存有益于提高訪問速度;而指針的方式,除了給結(jié)構(gòu)體申請空間之外,還得給結(jié)構(gòu)體里的指針成員申請空間。
使用指針的方式寫代碼會比柔性數(shù)組的方式會繁瑣一些,特別地,如果在釋放內(nèi)存的時候把順序弄反了,則結(jié)構(gòu)體里的指針成員所指向的內(nèi)存就釋放不掉,會造成內(nèi)存泄露。
使用位操作
1、使用位域
有些數(shù)據(jù)在存儲時并不需要占用一個完整的字節(jié),只需要占用一個或幾個二進制位即可。
比如:管理一些標(biāo)志位。
使用位域:
struct{ unsignedcharflag1:1; unsignedcharflag2:1; unsignedcharflag3:1; unsignedcharflag4:1; unsignedcharflag5:1; unsignedcharflag6:1; unsignedcharflag7:1; unsignedcharflag8:1; }flags;
優(yōu)于:
struct{ unsignedcharflag1; unsignedcharflag2; unsignedcharflag3; unsignedcharflag4; unsignedcharflag5; unsignedcharflag6; unsignedcharflag7; unsignedcharflag8; }flags;
2、使用位操作代替除法和乘法
使用位操作:
uint32_tval=1024; uint32_tdoubled=val<1;? uint32_t?halved?=?val?>>1;
優(yōu)于:
uint32_tval=1024; uint32_tdoubled=val*2 uint32_thalved=val/2
循環(huán)展開
有時候,可以犧牲一點代碼的簡潔度、減少循環(huán)控制語句的執(zhí)行頻率以提高性能。
無依賴的循環(huán)展開:
process(array[0]); process(array[1]); process(array[2]); process(array[3]);
優(yōu)于:
for(inti=0;i4;?i++)? { ????process(array[i]); }
有依賴的循環(huán)展開:
longcalc_sum(int*a,int*b) { longsum0=0; longsum1=0; longsum2=0; longsum3=0; for(inti=0;i250;?i?+=?4) ?{ ??sum0?+=?arr0[i?+?0]?*?arr1[i?+?0]; ??sum1?+=?arr0[i?+?1]?*?arr1[i?+?1]; ??sum2?+=?arr0[i?+?2]?*?arr1[i?+?2]; ??sum3?+=?arr0[i?+?3]?*?arr1[i?+?3]; ?} ? ?return?(sum0?+?sum1?+?sum2?+?sum3); }
優(yōu)于:
longcalc_sum(int*a,int*b) { longsum=0; for(inti=0;i1000;?i?++) ?{ ??sum?+=?arr0[i]?*?arr1[i]; ?} ? ?return?sum; }
盡可能把長的有依賴的代碼鏈分解成幾個可以在流水線執(zhí)行單元中并行執(zhí)行的沒有依賴的代碼鏈,提高流水線的連續(xù)性。通常4次展開為最佳方式。
使用內(nèi)聯(lián)函數(shù)
使用內(nèi)聯(lián)函數(shù)替換重復(fù)的短代碼,一方面,可以避免函數(shù)的回調(diào),加速了程序的執(zhí)行,利用指令緩存,增強局部訪問性;另一方面,可以方便代碼管理。
如:翻轉(zhuǎn)led的操作。
staticinlinevoidtoggle_led(uint8_tpin) { PORT^=1<使用合適的數(shù)據(jù)類型
首先使用合適的數(shù)據(jù)類型。
比如幾種數(shù)據(jù)類型都滿足需求的情況下,更小的可能并不是最合適的。
比如:素組索引的變量類型。
數(shù)組索引應(yīng)盡量采用int類型。
inti; for(i=0;i優(yōu)于:
chari; for(i=0;i定義為char類型,一般會有溢出的風(fēng)險,因此編譯器需要使用多余的指令判斷是否溢出;而使用int類型,一般編譯器默認不會超過這么大的循環(huán)次數(shù),從而減少了不必要的指令。
其它情況下,在滿足數(shù)據(jù)范圍的情況下,能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要用長整型(long int),能不使用浮點型(float)變量就不要使用浮點型變量。
多重循環(huán)優(yōu)化
長循環(huán)在最內(nèi)層:
for(col=0;col5;?col++) { ?for?(row?=?0;?row?100;?row++) ?{ ??sum?=?sum?+?a[row][col]; ?} }優(yōu)于長循環(huán)在最外層:
for(row=0;row100;?row++) { ?for(col=0;?col?5;?col++?) ?{ ??sum?=?sum?+?a[row][col]; ?} }在多重循環(huán)中,應(yīng)當(dāng)將最長的循環(huán)放在最內(nèi)層, 最短的循環(huán)放在最外層,以減少 CPU 跨切循環(huán)層的次數(shù)。
盡早退出循環(huán)
通常,循環(huán)并不需要全部都執(zhí)行。
例如,如果我們在從數(shù)組中查找一個特殊的值,一經(jīng)找到,我們應(yīng)該盡可能早的斷開循環(huán)。例如:如下循環(huán)從10000個整數(shù)中查找是否存在-99。
charfound=FALSE; for(i=0;i10000;?i++) { ????if?(list[i]?==?-99) ????{ ????????found?=?TRUE; ????} } ? if?(found)? { ????printf("Yes,?there?is?a?-99.?Hooray! "); }這段代碼無論我們是否查找得到,循環(huán)都會全部執(zhí)行完。更好的方法是一旦找到我們查找的數(shù)字就終止繼續(xù)查詢。把程序修改為:
found=FALSE; for(i=0;i10000;?i++) { ????if?(list[i]?==?-99) ????{ ????????found?=?TRUE; ????????break; ????} } ? if?(found)? { ????printf("Yes,?there?is?a?-99.?Hooray! "); }假如待查數(shù)據(jù)位于第23個位置上,程序便會執(zhí)行23次,從而節(jié)省9977次循環(huán)。
結(jié)構(gòu)體內(nèi)存對齊
必要時,手動對齊結(jié)構(gòu)體的內(nèi)存排列。
比如:
typedefstructtest_struct { chara; shortb; charc; intd; chare; }test_struct;該結(jié)構(gòu)體在32bit環(huán)境中,該結(jié)構(gòu)體所占的字節(jié)數(shù)為16。
可以手動調(diào)整各成員的位置來進行空白字節(jié)填充以達到對齊的效果。如:
typedefstructtest_struct { chara; charc; shortb; intd; chare; }test_struct;則結(jié)構(gòu)體變量test_s所占的字節(jié)數(shù)變?yōu)?2字節(jié),比原來的16字節(jié)省下了4個字節(jié)。
優(yōu)化中斷處理
確保中斷處理快速且盡可能短。
//中斷例程應(yīng)該盡量簡短 voidISR() { flag=true; }利用硬件特性
使用硬件模塊或特有指令來減輕CPU負擔(dān)。
//比如,直接使用DMA傳輸而不經(jīng)由CPU DMA_Config(&src,&dest,length); DMA_Start();以上就是本次的分享。一些優(yōu)化可能會增加代碼的復(fù)雜性或降低可讀性或其它方面的影響,因此在決定應(yīng)用優(yōu)化時,需權(quán)衡不同方面的影響。
審核編輯:黃飛
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19126瀏覽量
305201 -
cpu
+關(guān)注
關(guān)注
68文章
10863瀏覽量
211765 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62618 -
編譯器
+關(guān)注
關(guān)注
1文章
1634瀏覽量
49132
原文標(biāo)題:實用的嵌入式C代碼優(yōu)化技巧與經(jīng)驗
文章出處:【微信號:mcu149,微信公眾號:電子電路開發(fā)學(xué)習(xí)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論