版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、C代碼優(yōu)化方案 1、選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)選擇一種合適的數(shù)據(jù)結(jié)構(gòu)很重要,如果在一堆隨機存放的數(shù)中使用了大量的插入和刪除指令,那使用鏈表要快得多。數(shù)組與指針語句具有十分密切的關(guān)系,一般來說,指針比較靈活簡潔,而數(shù)組則比較直觀,容易理解。對于大部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,執(zhí)行效率更高。 在許多種情況下,可以用指針運算代替數(shù)組索引,這樣做常常能產(chǎn)生又快又短的代碼。與數(shù)組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數(shù)組時差異更明顯。下面的代碼作用是相同的,但是效率不一樣? 數(shù)組索引 指針運算 For(;) p=array A=arrayt+; for(;) a=*(
2、p+); 。 。 指針方法的優(yōu)點是,array的地址每次裝入地址p后,在每次循環(huán)中只需對p增量操作。在數(shù)組索引方法中,每次循環(huán)中都必須根據(jù)t值求數(shù)組下標的復雜運算。 2、使用盡量小的數(shù)據(jù)類型 能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要用長整型(long int),能不使用浮點型(float)變量就不要使用浮點型變量。當然,在定義變量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C編譯器并不報錯,但程序運行結(jié)果卻錯了,而且這樣的錯誤很難發(fā)現(xiàn)。 在ICCAVR中,可以在Options中設(shè)定使用printf參數(shù),盡量使用基本型參數(shù)(
3、%c、%d、%x、%X、%u和%s格式說明符),少用長整型參數(shù)(%ld、%lu、%lx和%lX格式說明符),至于浮點型的參數(shù)(%f)則盡量不要使用,其它C編譯器也一樣。在其它條件不變的情況下,使用%f參數(shù),會使生成的代碼的數(shù)量增加很多,執(zhí)行速度降低。3、減少運算的強度 (1)、查表(游戲程序員必修課) 一個聰明的游戲大蝦,基本上不會在自己的主循環(huán)里搞什么運算工作,絕對是先計算好了,再到循環(huán)里查表??聪旅娴睦樱号f代碼: long factorial(int i) if (i = 0) return 1; else return i * factorial(i - 1); 新代碼: static
4、 long factorial_table = 1, 1, 2, 6, 24, 120, 720 /* etc */ ; long factorial(int i) return factorial_tablei; 如果表很大,不好寫,就寫一個init函數(shù),在循環(huán)外臨時生成表格。(2)、求余運算 a=a%8; 可以改為: a=a&7; 說明:位操作只需一個指令周期即可完成,而大部分的C編譯器的“%”運算均是調(diào)用子程序來完成,代碼長、執(zhí)行速度慢。通常,只要求是求2n方的余數(shù),均可使用位操作的方法來代替。 (3)、平方運算a=pow(a, 2.0); 可以改為: a=a*a; 說明:在有內(nèi)置硬件乘
5、法器的單片機中(如51系列),乘法運算比求平方運算快得多,因為浮點數(shù)的求平方是通過調(diào)用子程序來實現(xiàn)的,在自帶硬件乘法器的AVR單片機中,如ATMega163中,乘法運算只需2個時鐘周期就可以完成。既使是在沒有內(nèi)置硬件乘法器的AVR單片機中,乘法運算的子程序比平方運算的子程序代碼短,執(zhí)行速度快。 如果是求3次方,如: a=pow(a,3。0); 更改為: a=a*a*a; 則效率的改善更明顯。 (4)、用移位實現(xiàn)乘除法運算 a=a*4; b=b/4; 可以改為: a=a2; 通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果乘以2n,都可以生成左移的代碼,而乘以其它的整數(shù)
6、或除以任何數(shù),均調(diào)用乘除法子程序。用移位的方法得到代碼比調(diào)用乘除法子程序生成的代碼效率高。實際上,只要是乘以或除以一個整數(shù),均可以用移位的方法得到結(jié)果,如: a=a*9 可以改為: a=(a3)+a采用運算量更小的表達式替換原來的表達式,下面是一個經(jīng)典例子: 舊代碼: x = w % 8; y = pow(x, 2.0); z = y * 33; for (i = 0;i MAX;i+) h = 14 * i; printf(%d, h); 新代碼: x = w & 7; /* 位操作比求余運算快*/ y = x * x; /* 乘法比平方運算快*/ z = (y 5) + y; /* 位移乘
7、法比乘法快 */ for (i = h = 0; i 0) while (*q (*r = a / *q) *q = (*q + *r) 1; *r = a - *q * *q; 推薦的代碼: / 假設(shè) q != r void isqrt(unsigned long a, unsigned long* q, unsigned long* r) unsigned long qq, rr; qq = a; if (a 0) while (qq (rr = a / qq) qq = (qq + rr) 1; rr = a - qq * qq; *q = qq; *r = rr; 5、循環(huán)優(yōu)化(1)、
8、充分分解小的循環(huán)要充分利用CPU的指令緩存,就要充分分解小的循環(huán)。特別是當循環(huán)體本身很小的時候,分解循環(huán)可以提高性能。注意:很多編譯器并不能自動分解循環(huán)。 不好的代碼: / 3D轉(zhuǎn)化:把矢量 V 和 4x4 矩陣 M 相乘 for (i = 0; i 4; i +) ri = 0; for (j = 0; j 4; j +) ri += Mji*Vj; 推薦的代碼: r0 = M00*V0 + M10*V1 + M20*V2 + M30*V3; r1 = M01*V0 + M11*V1 + M21*V2 + M31*V3; r2 = M02*V0 + M12*V1 + M22*V2 + M32
9、*V3; r3 = M03*V0 + M13*V1 + M23*V2 + M33*v3;(2)、提取公共部分對于一些不需要循環(huán)變量參加運算的任務可以把它們放到循環(huán)外面,這里的任務包括表達式、函數(shù)的調(diào)用、指針運算、數(shù)組訪問等,應該將沒有必要執(zhí)行多次的操作全部集合在一起,放到一個init的初始化程序中進行。 (3)、延時函數(shù)通常使用的延時函數(shù)均采用自加的形式: void delay (void) unsigned int i; for (i=0;i0;i-) ; 兩個函數(shù)的延時效果相似,但幾乎所有的C編譯對后一種函數(shù)生成的代碼均比前一種代碼少13個字節(jié),因為幾乎所有的MCU均有為0轉(zhuǎn)移的指令,采用
10、后一種方式能夠生成這類指令。在使用while循環(huán)時也一樣,使用自減指令控制循環(huán)會比使用自加指令控制循環(huán)生成的代碼更少13個字母。但是在循環(huán)中有通過循環(huán)變量“i”讀寫數(shù)組的指令時,使用預減循環(huán)有可能使數(shù)組超界,要引起注意。 (4)、while循環(huán)和dowhile循環(huán) 用while循環(huán)時有以下兩種循環(huán)形式: unsigned int i; i=0; while (i0); 在這兩種循環(huán)中,使用dowhile循環(huán)編譯后生成的代碼的長度短于while循環(huán)。 (6)、循環(huán)展開這是經(jīng)典的速度優(yōu)化,但許多編譯程序(如gcc -funroll-loops)能自動完成這個事,所以現(xiàn)在你自己來優(yōu)化這個顯得效果不明
11、顯。 舊代碼: for (i = 0; i 100; i+) do_stuff(i); 新代碼: for (i = 0; i 100; ) do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; do_stuff(i); i+; 可以看出,新代碼里比較指令由100次降低為10次,循環(huán)時間節(jié)約了90%。不過注意:對于中間變量或結(jié)果被更改的循環(huán),編譯程
12、序往往拒絕展開,(怕?lián)熑螁h),這時候就需要你自己來做展開工作了。 還有一點請注意,在有內(nèi)部指令cache的CPU上(如MMX芯片),因為循環(huán)展開的代碼很大,往往cache溢出,這時展開的代碼會頻繁地在CPU 的cache和內(nèi)存之間調(diào)來調(diào)去,又因為cache速度很高,所以此時循環(huán)展開反而會變慢。還有就是循環(huán)展開會影響矢量運算優(yōu)化。 (6)、循環(huán)嵌套 把相關(guān)循環(huán)放到一個循環(huán)里,也會加快速度。 舊代碼: for (i = 0; i MAX; i+) /* initialize 2d array to 0s */ for (j = 0; j MAX; j+) aij = 0.0; for (i =
13、0; i MAX; i+) /* put 1s along the diagonal */ aii = 1.0; 新代碼: for (i = 0; i MAX; i+) /* initialize 2d array to 0s */ for (j = 0; j MAX; j+) aij = 0.0; aii = 1.0; /* put 1s along the diagonal */ (7)、Switch語句中根據(jù)發(fā)生頻率來進行case排序 Switch 可能轉(zhuǎn)化成多種不同算法的代碼。其中最常見的是跳轉(zhuǎn)表和比較鏈/樹。當switch用比較鏈的方式轉(zhuǎn)化時,編譯器會產(chǎn)生if-else-if的嵌套代
14、碼,并按照順序進行比較,匹配時就跳轉(zhuǎn)到滿足條件的語句執(zhí)行。所以可以對case的值依照發(fā)生的可能性進行排序,把最有可能的放在第一位,這樣可以提高性能。此外,在case中推薦使用小的連續(xù)的整數(shù),因為在這種情況下,所有的編譯器都可以把switch 轉(zhuǎn)化成跳轉(zhuǎn)表。 不好的代碼: int days_in_month, short_months, normal_months, long_months; 。 switch (days_in_month) case 28: case 29: short_months +; break; case 30: normal_months +; break; case
15、 31: long_months +; break; default: cout month has fewer than 28 or more than 31 days endl; break; 推薦的代碼: int days_in_month, short_months, normal_months, long_months; 。 switch (days_in_month) case 31: long_months +; break; case 30: normal_months +; break; case 28: case 29: short_months +; break; def
16、ault: cout month has fewer than 28 or more than 31 days type) case FREQUENT_MSG1: handleFrequentMsg(); break; case FREQUENT_MSG2: handleFrequentMsg2(); break; 。 case FREQUENT_MSGn: handleFrequentMsgn(); break; default: /嵌套部分用來處理不經(jīng)常發(fā)生的消息 switch (pMsg-type) case INFREQUENT_MSG1: handleInfrequentMsg1()
17、; break; case INFREQUENT_MSG2: handleInfrequentMsg2(); break; 。 case INFREQUENT_MSGm: handleInfrequentMsgm(); break; 如果switch中每一種情況下都有很多的工作要做,那么把整個switch語句用一個指向函數(shù)指針的表來替換會更加有效,比如下面的switch語句,有三種情況: enum MsgTypeMsg1, Msg2, Msg3 switch (ReceiveMessage() case Msg1; 。 case Msg2; 。 case Msg3; 。 為了提高執(zhí)行速度,用下
18、面這段代碼來替換這個上面的switch語句。 /*準備工作*/ int handleMsg1(void); int handleMsg2(void); int handleMsg3(void); /*創(chuàng)建一個函數(shù)指針數(shù)組*/ int (*MsgFunction )()=handleMsg1, handleMsg2, handleMsg3; /*用下面這行更有效的代碼來替換switch語句*/ status=MsgFunctionReceiveMessage()(); (9)、循環(huán)轉(zhuǎn)置有些機器對JNZ(為0轉(zhuǎn)移)有特別的指令處理,速度非??欤绻愕难h(huán)對方向不敏感,可以由大向小循環(huán)。 舊代碼:
19、 for (i = 1; i = MAX; i+) 。 新代碼: i = MAX+1; while (-i) 。 不過千萬注意,如果指針操作使用了i值,這種方法可能引起指針越界的嚴重錯誤(i = MAX+1;)。當然你可以通過對i做加減運算來糾正,但是這樣就起不到加速的作用,除非類似于以下情況: 舊代碼: char aMAX+5; for (i = 1; i = MAX; i+) *(a+i+4)=0; 新代碼: i = MAX+1; while (-i) *(a+i+4)=0; (10)、公用代碼塊一些公用處理模塊,為了滿足各種不同的調(diào)用需要,往往在內(nèi)部采用了大量的if-then-else結(jié)
20、構(gòu),這樣很不好,判斷語句如果太復雜,會消耗大量的時間的,應該盡量減少公用代碼塊的使用。(任何情況下,空間優(yōu)化和時間優(yōu)化都是對立的-東樓)。當然,如果僅僅是一個(3=x)之類的簡單判斷,適當使用一下,也還是允許的。記住,優(yōu)化永遠是追求一種平衡,而不是走極端。 (11)提升循環(huán)的性能 要提升循環(huán)的性能,減少多余的常量計算非常有用(比如,不隨循環(huán)變化的計算)。 不好的代碼(在for()中包含不變的if(): for( i 。 ) if( CONSTANT0 ) DoWork0( i ); / 假設(shè)這里不改變CONSTANT0的值 else DoWork1( i ); / 假設(shè)這里不改變CONSTAN
21、T0的值 推薦的代碼: if( CONSTANT0 ) for( i 。 ) DoWork0( i ); else for( i 。 ) DoWork1( i ); 如果已經(jīng)知道if()的值,這樣可以避免重復計算。雖然不好的代碼中的分支可以簡單地預測,但是由于推薦的代碼在進入循環(huán)前分支已經(jīng)確定,就可以減少對分支預測的依賴。 (12)、選擇好的無限循環(huán) 在編程中,我們常常需要用到無限循環(huán),常用的兩種方法是while (1) 和 for (;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯后的代碼: 編譯前: while (1); 編譯后: mov eax,1 test eax,ea
22、x je foo+23h jmp foo+18h 編譯前: for (;); 編譯后: jmp foo+23h 顯然,for (;)指令少,不占用寄存器,而且沒有判斷、跳轉(zhuǎn),比while (1)好。6、提高CPU的并行性 (1)使用并行代碼盡可能把長的有依賴的代碼鏈分解成幾個可以在流水線執(zhí)行單元中并行執(zhí)行的沒有依賴的代碼鏈。很多高級語言,包括C+,并不對產(chǎn)生的浮點表達式重新排序,因為那是一個相當復雜的過程。需要注意的是,重排序的代碼和原來的代碼在代碼上一致并不等價于計算結(jié)果一致,因為浮點操作缺乏精確度。在一些情況下,這些優(yōu)化可能導致意料之外的結(jié)果。幸運的是,在大部分情況下,最后結(jié)果可能只有最不
23、重要的位(即最低位)是錯誤的。 不好的代碼: double a100, sum; int i; sum = 0.0f; for (i=0; i100; i+) sum += ai; 推薦的代碼: double a100, sum1, sum2, sum3, sum4, sum; int i; sum1 = sum2 = sum3 = sum4 = 0.0; for (i = 0; i 100; i += 4) sum1 += ai; sum2 += ai+1; sum3 += ai+2; sum4 += ai+3; sum = (sum4+sum3)+(sum1+sum2); 要注意的是:使用
24、4路分解是因為這樣使用了4段流水線浮點加法,浮點加法的每一個段占用一個時鐘周期,保證了最大的資源利用率。 (2)避免沒有必要的讀寫依賴 當數(shù)據(jù)保存到內(nèi)存時存在讀寫依賴,即數(shù)據(jù)必須在正確寫入后才能再次讀取。雖然AMD Athlon等CPU有加速讀寫依賴延遲的硬件,允許在要保存的數(shù)據(jù)被寫入內(nèi)存前讀取出來,但是,如果避免了讀寫依賴并把數(shù)據(jù)保存在內(nèi)部寄存器中,速度會更快。在一段很長的又互相依賴的代碼鏈中,避免讀寫依賴顯得尤其重要。如果讀寫依賴發(fā)生在操作數(shù)組時,許多編譯器不能自動優(yōu)化代碼以避免讀寫依賴。所以推薦程序員手動去消除讀寫依賴,舉例來說,引進一個可以保存在寄存器中的臨時變量。這樣可以有很大的性能
25、提升。下面一段代碼是一個例子: 不好的代碼: float xVECLEN, yVECLEN, zVECLEN; 。 for (unsigned int k = 1; k VECLEN; k +) xk = xk-1 + yk; for (k = 1; k VECLEN; k+) xk = zk * (yk - xk-1); 推薦的代碼: float xVECLEN, yVECLEN, zVECLEN; 。 float t(x0); for (unsigned int k = 1; k VECLEN; k +) t = t + yk; xk = t; t = x0; for (k = 1; k
26、b-c4-aardvark + a-b-c4-baboon + a-b-c4-cheetah + a-b-c4-dog; 新代碼: struct animals * temp = a-b-c4; total = temp-aardvark + temp-baboon + temp-cheetah + temp-dog; 一些老的C語言編譯器不做聚合優(yōu)化,而符合ANSI規(guī)范的新的編譯器可以自動完成這個優(yōu)化,看例子: float a, b, c, d, f, g; 。 a = b / c * d; f = b * g / c; 這種寫法當然要得,但是沒有優(yōu)化 float a, b, c, d, f
27、, g; 。 a = b / c * d; f = b / c * g; 如果這么寫的話,一個符合ANSI規(guī)范的新的編譯器可以只計算b/c一次,然后將結(jié)果代入第二個式子,節(jié)約了一次除法運算。 8、函數(shù)優(yōu)化 (1)Inline函數(shù) 在C+中,關(guān)鍵字Inline可以被加入到任何函數(shù)的聲明中。這個關(guān)鍵字請求編譯器用函數(shù)內(nèi)部的代碼替換所有對于指出的函數(shù)的調(diào)用。這樣做在兩個方面快于函數(shù)調(diào)用:第一,省去了調(diào)用指令需要的執(zhí)行時間;第二,省去了傳遞變元和傳遞過程需要的時間。但是使用這種方法在優(yōu)化程序速度的同時,程序長度變大了,因此需要更多的ROM。使用這種優(yōu)化在Inline函數(shù)頻繁調(diào)用并且只包含幾行代碼的時候
28、是最有效的。 (2)不定義不使用的返回值函數(shù)定義并不知道函數(shù)返回值是否被使用,假如返回值從來不會被用到,應該使用void來明確聲明函數(shù)不返回任何值。 (3)減少函數(shù)調(diào)用參數(shù) 使用全局變量比函數(shù)傳遞參數(shù)更加有效率。這樣做去除了函數(shù)調(diào)用參數(shù)入棧和函數(shù)完成后參數(shù)出棧所需要的時間。然而決定使用全局變量會影響程序的模塊化和重入,故要慎重使用。 (4)所有函數(shù)都應該有原型定義一般來說,所有函數(shù)都應該有原型定義。原型定義可以傳達給編譯器更多的可能用于優(yōu)化的信息。 (5)盡可能使用常量(const)盡可能使用常量(const)。C+ 標準規(guī)定,如果一個const聲明的對象的地址不被獲取,允許編譯器不對它分配儲
29、存空間。這樣可以使代碼更有效率,而且可以生成更好的代碼。 (6)把本地函數(shù)聲明為靜態(tài)的(static)如果一個函數(shù)只在實現(xiàn)它的文件中被使用,把它聲明為靜態(tài)的(static)以強制使用內(nèi)部連接。否則,默認的情況下會把函數(shù)定義為外部連接。這樣可能會影響某些編譯器的優(yōu)化比如,自動內(nèi)聯(lián)。 9、采用遞歸與LISP之類的語言不同,C語言一開始就病態(tài)地喜歡用重復代碼循環(huán),許多C程序員都是除非算法要求,堅決不用遞歸。事實上,C編譯器們對優(yōu)化遞歸調(diào)用一點都不反感,相反,它們還很喜歡干這件事。只有在遞歸函數(shù)需要傳遞大量參數(shù),可能造成瓶頸的時候,才應該使用循環(huán)代碼,其他時候,還是用遞歸好些。 10、變量(1)reg
30、ister變量在聲明局部變量的時候可以使用register關(guān)鍵字。這就使得編譯器把變量放入一個多用途的寄存器中,而不是在堆棧中,合理使用這種方法可以提高執(zhí)行速度。函數(shù)調(diào)用越是頻繁,越是可能提高代碼的速度。 在最內(nèi)層循環(huán)避免使用全局變量和靜態(tài)變量,除非你能確定它在循環(huán)周期中不會動態(tài)變化,大多數(shù)編譯器優(yōu)化變量都只有一個辦法,就是將他們置成寄存器變量,而對于動態(tài)變量,它們干脆放棄對整個表達式的優(yōu)化。盡量避免把一個變量地址傳遞給另一個函數(shù),雖然這個還很常用。C語言的編譯器們總是先假定每一個函數(shù)的變量都是內(nèi)部變量,這是由它的機制決定的,在這種情況下,它們的優(yōu)化完成得最好。但是,一旦一個變量有可能被別的函
31、數(shù)改變,這幫兄弟就再也不敢把變量放到寄存器里了,嚴重影響速度。看例子: a = b(); c(&d); 因為d的地址被c函數(shù)使用,有可能被改變,編譯器不敢把它長時間的放在寄存器里,一旦運行到c(&d),編譯器就把它放回內(nèi)存,如果在循環(huán)里,會造成N次頻繁的在內(nèi)存和寄存器之間讀寫d的動作,眾所周知,CPU在系統(tǒng)總線上的讀寫速度慢得很。比如你的賽楊300,CPU主頻300,總線速度最多66M,為了一個總線讀,CPU可能要等4-5個周期,得。得。得。想起來都打顫。 (2)、同時聲明多個變量優(yōu)于單獨聲明變量(3)、短變量名優(yōu)于長變量名,應盡量使變量名短一點(4)、在循環(huán)開始前聲明變量11、使用嵌套的if
32、結(jié)構(gòu)在if結(jié)構(gòu)中如果要判斷的并列條件較多,最好將它們拆分成多個if結(jié)構(gòu),然后嵌套在一起,這樣可以避免無謂的判斷。 說明: 上面的優(yōu)化方案由王全明收集整理。很多資料來源與網(wǎng)上,出處不祥,在此對所有作者一并致謝! 該方案主要是考慮到在嵌入式開發(fā)中對程序執(zhí)行速度的要求特別高,所以該方案主要是為了優(yōu)化程序的執(zhí)行速度。 注意:優(yōu)化是有側(cè)重點的,優(yōu)化是一門平衡的藝術(shù),它往往要以犧牲程序的可讀性或者增加代碼長度為代價。 (任何情況下,空間優(yōu)化和時間優(yōu)化都是對立的-東樓)。 嵌入式系統(tǒng)編程中的代碼優(yōu)化 嵌入式系統(tǒng)一般指非PC系統(tǒng),通常完成一種或多種特定的計算機功能。它是以應用為中心,軟硬件可裁減的,適應應用系
33、統(tǒng)對功能,可靠性,成本,體積,功耗等綜合性要求的專用計算機系統(tǒng)。簡單的說類似于PC中的BIOS的工作方式,具有軟件代碼小、高度自動化、響應速度快等特點。 特別適合于要求實時和多任務的應用體系。嵌入式實時系統(tǒng)是目前蓬勃發(fā)展的行業(yè)之一。但是,實時嵌入式系統(tǒng)的特點使得其軟件受時間和空間的嚴格限制,加上運行環(huán)境復雜,使得嵌入式系統(tǒng)軟件的開發(fā)變得異常困難。 為了設(shè)計一個滿足功能、性能和時間要求的安全可靠的高性能嵌入式系統(tǒng),編程語言的選擇十分重要。 1、嵌入式系統(tǒng)中編程語言的選擇 因為匯編語言編寫的代碼難懂,從而不好維護和難于調(diào)試,且只能針對特定的體系結(jié)構(gòu)和處理器移植性差, 所以既不宜在復雜系統(tǒng)中使用,又
34、不便于實現(xiàn)軟件重用;而高級語言具有良好的通用性和豐富的軟件支持,可移植性好、易于維護,因此高級語言編程具有許多優(yōu)勢。隨著嵌入式系統(tǒng)應用范圍的不斷擴大和嵌入式實時操作系統(tǒng)RTOS(Real Time Operating System)的廣泛使用,高級語言編程已是嵌入式系統(tǒng)設(shè)計的必然趨勢。但是 不排除一些軟件模塊仍用匯編語言來寫,這可以使程序更加有效。雖然C/C+編譯器對代碼進行了優(yōu)化,但是適當?shù)氖褂脙?nèi)聯(lián)匯編指令可以有效的提高整個系統(tǒng)運行的效率。 目前,在嵌入式系統(tǒng)開發(fā)過程中使用的語言種類很多,但僅有少數(shù)幾種語言得到了比較廣泛的應用。其中C和C+是應用最廣泛的。C+在支持現(xiàn)代軟件工程、 OOP(O
35、bject Oriented Programming,面向?qū)ο蟮某绦蛟O(shè)計)、結(jié)構(gòu)化等方面對C進行了卓有成效的改進,但在程序代碼容量、執(zhí)行速度、 程序復雜程度等方面比C語言程序性能差一些。由于C語言既有低級語言的直接控制硬件的能力,又有高級語言的靈活性,是目前在嵌入式系統(tǒng)中應用最廣泛的編程語言。隨著網(wǎng)絡技術(shù)和嵌入式技術(shù)的不斷發(fā)展,Java的應用也得到廣泛應用。 2、實時程序設(shè)計中代碼的優(yōu)化 在嵌入式的系統(tǒng)開發(fā)中,出于對低價產(chǎn)品的需求, 硬件的設(shè)計者需要提供剛好足夠的存儲器和完成工作的處理能力。所以在嵌入式軟件設(shè)計的最后一個階段則變成了對代碼的優(yōu)化。 代碼優(yōu)化的目標是體積小和速度快,可以從算法、數(shù)
36、據(jù)和指令流三方面來考慮。 算法優(yōu)化 大多數(shù)情況下,速度同內(nèi)存(或者是性能,比如說壓縮性能)是不可兼得的。目前程序加速的常用算法一個大方面就是利用查表來避免計算(比如在jpg有huffman碼表,在YUV到RGB變換也有變換表)這樣原來的復雜計算現(xiàn)在僅僅查表就可以了,雖然浪費了內(nèi)存,不過速度顯著提升。此外在編寫程序時還要注意提高效率,例如: Switch語句中根據(jù)發(fā)生頻率來進行case排序 switch語句是一個普通的編程技術(shù),編譯器會產(chǎn)生if-else-if的嵌套代碼,并按照順序進行比較,發(fā)現(xiàn)匹配時,就跳轉(zhuǎn)到滿足條件的語句執(zhí)行。使用時需要注意。每一個由機器語言實現(xiàn)的測試和跳轉(zhuǎn)僅僅是為了決定下一
37、步要做什么,就把寶貴的處理器時間耗盡。為了提高速度,設(shè)法根據(jù)具體的情況按照它們發(fā)生的相對頻率排序。換句話說,把最可能發(fā)生的情況放在第一位,最不可能的情況放在最后。 將大的switch語句轉(zhuǎn)為嵌套switch語句 當switch語句中的case標號很多時,為了減少比較的次數(shù),明智的做法是把大switch語句轉(zhuǎn)為嵌套switch語句。把發(fā)生頻率高的case 標號放在一個switch語句中,并且是嵌套switch語句的最外層,發(fā)生相對頻率相對低的case標號放在另一個switch語句中。 如果switch中每一種情況下都有很多的工作要做,那么把整個switch語句用一個指向函數(shù)指針的表來替換會更加有
38、效。 用指針代替數(shù)組 在許多種情況下,可以用指針運算代替數(shù)組索引,這樣做常常能產(chǎn)生又快又短的代碼。與數(shù)組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數(shù)組時差異更明顯。下面的代碼作用是相同的,但是效率不一樣。 數(shù)組索引 指針運算 For(;) p=array A=arrayr+; for(;) a=*(p+); . . 指針方法的優(yōu)點是,array的地址每次裝入地址p后,在每次循環(huán)中只需對p增量操作。在數(shù)組索引方法中,每次循環(huán)中都必須進行基于r值求數(shù)組下標的復雜運算。 使用宏函數(shù)而不是函數(shù)。例如: #define bwMCDR2_ADDRESS 4 #define bsMCDR2_
39、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); 函數(shù)和宏函數(shù)的區(qū)別就在于,宏函數(shù)占用了大量的空間,而函數(shù)占用了時間。函數(shù)
40、調(diào)用是要使用系統(tǒng)的棧來保存數(shù)據(jù)的,如果編譯器里有棧檢查選項,一般在函數(shù)的頭會嵌入一些匯編語句對當前棧進行檢查;同時,CPU也要在函數(shù)調(diào)用時保存和恢復當前的現(xiàn)場,進行壓棧和彈棧操作,所以,函數(shù)調(diào)用需要一些CPU時間。而宏函數(shù)不存在這個問題。宏函數(shù)僅僅作為預先寫好的代碼嵌入到當前程序,不會產(chǎn)生函數(shù)調(diào)用,所以僅僅是占用了空間,在頻繁調(diào)用同一個宏函數(shù)的時候,該現(xiàn)象尤其突出。Data optimization數(shù)據(jù)優(yōu)化 比算法優(yōu)化層低一級的是數(shù)據(jù)優(yōu)化層,我們可以通過改變算法使用的數(shù)據(jù)類型來優(yōu)化算法。主要的目的是使處理的數(shù)據(jù)和目標結(jié)構(gòu)的特性相一致。這項優(yōu)化不需要大量的代碼重寫,并獨立于算法優(yōu)化的執(zhí)行而執(zhí)行.
41、例如: 確定浮點型變量和表達式是 float 型 為了讓編譯器產(chǎn)生更好的代碼,必須確定浮點型變量和表達式是 float 型的。要特別注意的是,以 ;F; 或 ;f; 為后綴(比如:2.718f)的浮點常量才是 float 型,否則默認是 double 型。為了避免 float 型參數(shù)自動轉(zhuǎn)化為 double,請在函數(shù)聲明時使用 float。 使用32位的數(shù)據(jù)類型 編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigne
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 圍棋活動策劃預算方案(3篇)
- 社區(qū)教育活動方案策劃(3篇)
- 洗浴湯泉施工方案(3篇)
- 影城活動布置方案策劃(3篇)
- 光模塊施工方案(3篇)
- 活動策劃方案班級疫情(3篇)
- 一線工作法建立調(diào)研工作制度
- 2025年高職(建筑工程技術(shù))建筑施工技術(shù)試題
- 2025年大學生態(tài)學(系統(tǒng)實操技術(shù))試題及答案
- 2025年大學人工智能(機器學習基礎(chǔ))試題及答案
- 2026年馬年德育實踐作業(yè)(圖文版)
- 醫(yī)院實習生安全培訓課課件
- 四川省成都市武侯區(qū)西川中學2024-2025學年八上期末數(shù)學試卷(解析版)
- 2026年《必背60題》抖音本地生活BD經(jīng)理高頻面試題包含詳細解答
- 2024人教版七年級數(shù)學上冊全冊教案
- GB/T 20033.3-2006人工材料體育場地使用要求及檢驗方法第3部分:足球場地人造草面層
- GB/T 18997.2-2020鋁塑復合壓力管第2部分:鋁管對接焊式鋁塑管
- GB/T 10067.47-2014電熱裝置基本技術(shù)條件第47部分:真空熱處理和釬焊爐
- 狀語從句精講課件
- JJG544-2011《壓力控制器檢定規(guī)程》規(guī)程試題試題
- 施工現(xiàn)場車輛進出沖洗記錄
評論
0/150
提交評論