18video性欧美19sex,欧美高清videosddfsexhd,性少妇videosexfreexxx片中国,激情五月激情综合五月看花,亚洲人成网77777色在线播放

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

嵌入式代碼高效運(yùn)行指南

嵌入式開(kāi)發(fā)愛(ài)好者 ? 來(lái)源:嵌入式開(kāi)發(fā)愛(ài)好者 ? 2023-01-06 15:32 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

嵌入式C語(yǔ)言之所以經(jīng)久不衰,在于它的運(yùn)行效率很高,想要高效運(yùn)行代碼,除了編譯器幫忙優(yōu)化,關(guān)鍵還要靠自己“優(yōu)化”代碼。

1、選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)

選擇一種合適的數(shù)據(jù)結(jié)構(gòu)很重要,如果在一堆隨機(jī)存放的數(shù)中使用了大量的插入和刪除指令,那使用鏈表要快得多。數(shù)組與指針語(yǔ)句具有十分密切的關(guān)系,一般來(lái)說(shuō),指針比較靈活簡(jiǎn)潔,而數(shù)組則比較直觀(guān),容易理解。對(duì)于大部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,執(zhí)行效率更高。 在許多種情況下,可以用指針運(yùn)算代替數(shù)組索引,這樣做常常能產(chǎn)生又快又短的代碼。與數(shù)組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數(shù)組時(shí)差異更明顯。下面的代碼作用是相同的,但是效率不一樣。

	
		數(shù)組索引 指針運(yùn)算 For(;;){ p=array A=array[t++]; for(;;){ a=*(p++); 。。。。。。。。。。。。。。。 } }
		指針?lè)椒ǖ膬?yōu)點(diǎn)是,array的地址每次裝入地址p后,在每次循環(huán)中只需對(duì)p增量操作。在數(shù)組索引方法中,每次循環(huán)中都必須根據(jù)t值求數(shù)組下標(biāo)的復(fù)雜運(yùn)算。

2、使用盡量小的數(shù)據(jù)類(lèi)型

能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來(lái)定義;能夠使用整型變量定義的變量就不要用長(zhǎng)整型(long int),能不使用浮點(diǎn)型(float)變量就不要使用浮點(diǎn)型變量。當(dāng)然,在定義變量后不要超過(guò)變量的作用范圍,如果超過(guò)變量的范圍賦值,C編譯器并不報(bào)錯(cuò),但程序運(yùn)行結(jié)果卻錯(cuò)了,而且這樣的錯(cuò)誤很難發(fā)現(xiàn)。 在ICCAVR中,可以在Options中設(shè)定使用printf參數(shù),盡量使用基本型參數(shù)(%c、%d、%x、%X、%u和%s格式說(shuō)明符),少用長(zhǎng)整型參數(shù)(%ld、%lu、%lx和%lX格式說(shuō)明符),至于浮點(diǎn)型的參數(shù)(%f)則盡量不要使用,其它C編譯器也一樣。在其它條件不變的情況下,使用%f參數(shù),會(huì)使生成的代碼的數(shù)量增加很多,執(zhí)行速度降低。

3、減少運(yùn)算的強(qiáng)度

(1)、查表(游戲程序員必修課)

一個(gè)聰明的游戲大蝦,基本上不會(huì)在自己的主循環(huán)里搞什么運(yùn)算工作,絕對(duì)是先計(jì)算好了,再到循環(huán)里查表??聪旅娴睦樱? 舊代碼:

	
		long factorial(int i) { if (i == 0) return 1; else return i * factorial(i - 1); }
		新代碼:

	
		static long factorial_table[] = {1, 1, 2, 6, 24, 120, 720 /* etc */ }; long factorial(int i) { return factorial_table[i]; }
		如果表很大,不好寫(xiě),就寫(xiě)一個(gè)init函數(shù),在循環(huán)外臨時(shí)生成表格。

(2)求余運(yùn)算


	
		a=a%8;
		可以改為:

	
		a=a&7;
		說(shuō)明:位操作只需一個(gè)指令周期即可完成,而大部分的C編譯器的“%”運(yùn)算均是調(diào)用子程序來(lái)完成,代碼長(zhǎng)、執(zhí)行速度慢。通常,只要求是求2n方的余數(shù),均可使用位操作的方法來(lái)代替。

(3)平方運(yùn)算


	
		a=pow(a, 2.0);
		可以改為:

	
		a=a*a;
		說(shuō)明:在有內(nèi)置硬件乘法器的單片機(jī)中(如51系列),乘法運(yùn)算比求平方運(yùn)算快得多,因?yàn)楦↑c(diǎn)數(shù)的求平方是通過(guò)調(diào)用子程序來(lái)實(shí)現(xiàn)的,在自帶硬件乘法器的AVR單片機(jī)中,如ATMega163中,乘法運(yùn)算只需2個(gè)時(shí)鐘周期就可以完成。既使是在沒(méi)有內(nèi)置硬件乘法器的AVR單片機(jī)中,乘法運(yùn)算的子程序比平方運(yùn)算的子程序代碼短,執(zhí)行速度快。
		如果是求3次方,如:

	
		a=pow(a,3.0);
		更改為:

	
		a=a*a*a;
		則效率的改善更明顯。

(4)用移位實(shí)現(xiàn)乘除法運(yùn)算


	
		a=a*4; b=b/4;
		可以改為:

	
		a=a<<2; b=b>>2;
		通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果乘以2n,都可以生成左移的代碼,而乘以其它的整數(shù)或除以任何數(shù),均調(diào)用乘除法子程序。用移位的方法得到代碼比調(diào)用乘除法子程序生成的代碼效率高。實(shí)際上,只要是乘以或除以一個(gè)整數(shù),均可以用移位的方法得到結(jié)果,如:

	
		a=a*9
		可以改為:

	
		a=(a<<3)+a
		采用運(yùn)算量更小的表達(dá)式替換原來(lái)的表達(dá)式,下面是一個(gè)經(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ùn)算快*/ y = x * x; /* 乘法比平方運(yùn)算快*/ z = (y << 5) + y; /* 位移乘法比乘法快 */ for (i = h = 0; i < MAX; i++) { h += 14; /* 加法比乘法快 */ printf("%d",h); }

(5)避免不必要的整數(shù)除法

整數(shù)除法是整數(shù)運(yùn)算中最慢的,所以應(yīng)該盡可能避免。一種可能減少整數(shù)除法的地方是連除,這里除法可以由乘法代替。這個(gè)替換的副作用是有可能在算乘積時(shí)會(huì)溢出,所以只能在一定范圍的除法中使用。 不好的代碼:

	
		int i, j, k, m; m = i / j / k;
		推薦的代碼:

	
		int i, j, k, m; m = i / (j * k);

(6)使用增量和減量操作符

在使用到加一和減一操作時(shí)盡量使用增量和減量操作符,因?yàn)樵隽糠Z(yǔ)句比賦值語(yǔ)句更快,原因在于對(duì)大多數(shù)CPU來(lái)說(shuō),對(duì)內(nèi)存字的增、減量操作不必明顯地使用取內(nèi)存和寫(xiě)內(nèi)存的指令,比如下面這條語(yǔ)句:

	
		x=x+1;
		模仿大多數(shù)微機(jī)匯編語(yǔ)言為例,產(chǎn)生的代碼類(lèi)似于:

	
		move A,x ;把x從內(nèi)存取出存入累加器A add A,1 ;累加器A加1 store x ;把新值存回x
		如果使用增量操作符,生成的代碼如下:

	
		incr x ;x加1
		顯然,不用取指令和存指令,增、減量操作執(zhí)行的速度加快,同時(shí)長(zhǎng)度也縮短了。

(7)使用復(fù)合賦值表達(dá)式

復(fù)合賦值表達(dá)式(如a-=1及a+=1等)都能夠生成高質(zhì)量的程序代碼。

(8)提取公共的子表達(dá)式

在某些情況下,C++編譯器不能從浮點(diǎn)表達(dá)式中提出公共的子表達(dá)式,因?yàn)檫@意味著相當(dāng)于對(duì)表達(dá)式重新排序。需要特別指出的是,編譯器在提取公共子表達(dá)式前不能按照代數(shù)的等價(jià)關(guān)系重新安排表達(dá)式。這時(shí),程序員要手動(dòng)地提出公共的子表達(dá)式(在VC.NET里有一項(xiàng)“全局優(yōu)化”選項(xiàng)可以完成此工作,但效果就不得而知了)。 不好的代碼:

	
		float a, b, c, d, e, f; 。。。 e = b * c / d; f = b / d * a;
		推薦的代碼:

	
		float a, b, c, d, e, f; 。。。 const float t(b / d); e = c * t; f = a * t;
		不好的代碼:

	
		float a, b, c, e, f; 。。。 e = a / c; f = b / c;
		推薦的代碼:

	
		float a, b, c, e, f; 。。。 const float t(1.0f / c); e = a * t; f = b * t;

4、結(jié)構(gòu)體成員的布局

很多編譯器有“使結(jié)構(gòu)體字,雙字或四字對(duì)齊”的選項(xiàng)。但是,還是需要改善結(jié)構(gòu)體成員的對(duì)齊,有些編譯器可能分配給結(jié)構(gòu)體成員空間的順序與他們聲明的不同。但是,有些編譯器并不提供這些功能,或者效果不好。所以,要在付出最少代價(jià)的情況下實(shí)現(xiàn)最好的結(jié)構(gòu)體和結(jié)構(gòu)體成員對(duì)齊,建議采取下列方法:

(1)按數(shù)據(jù)類(lèi)型的長(zhǎng)度排序

把結(jié)構(gòu)體的成員按照它們的類(lèi)型長(zhǎng)度排序,聲明成員時(shí)把長(zhǎng)的類(lèi)型放在短的前面。編譯器要求把長(zhǎng)型數(shù)據(jù)類(lèi)型存放在偶數(shù)地址邊界。在申明一個(gè)復(fù)雜的數(shù)據(jù)類(lèi)型 (既有多字節(jié)數(shù)據(jù)又有單字節(jié)數(shù)據(jù)) 時(shí),應(yīng)該首先存放多字節(jié)數(shù)據(jù),然后再存放單字節(jié)數(shù)據(jù),這樣可以避免內(nèi)存的空洞。編譯器自動(dòng)地把結(jié)構(gòu)的實(shí)例對(duì)齊在內(nèi)存的偶數(shù)邊界。

(2)把結(jié)構(gòu)體填充成最長(zhǎng)類(lèi)型長(zhǎng)度的整倍數(shù)

把結(jié)構(gòu)體填充成最長(zhǎng)類(lèi)型長(zhǎng)度的整倍數(shù)。照這樣,如果結(jié)構(gòu)體的第一個(gè)成員對(duì)齊了,所有整個(gè)結(jié)構(gòu)體自然也就對(duì)齊了。下面的例子演示了如何對(duì)結(jié)構(gòu)體成員進(jìn)行重新排序: 不好的代碼,普通順序:

	
		struct { char a[5]; long k; double x; } baz;
		推薦的代碼,新的順序并手動(dòng)填充了幾個(gè)字節(jié):

	
		struct { double x; long k; char a[5]; char pad[7]; } baz;
		這個(gè)規(guī)則同樣適用于類(lèi)的成員的布局。

(3)按數(shù)據(jù)類(lèi)型的長(zhǎng)度排序本地變量

當(dāng)編譯器分配給本地變量空間時(shí),它們的順序和它們?cè)谠创a中聲明的順序一樣,和上一條規(guī)則一樣,應(yīng)該把長(zhǎng)的變量放在短的變量前面。如果第一個(gè)變量對(duì)齊了,其它變量就會(huì)連續(xù)的存放,而且不用填充字節(jié)自然就會(huì)對(duì)齊。有些編譯器在分配變量時(shí)不會(huì)自動(dòng)改變變量順序,有些編譯器不能產(chǎn)生4字節(jié)對(duì)齊的棧,所以4字節(jié)可能不對(duì)齊。下面這個(gè)例子演示了本地變量聲明的重新排序: 不好的代碼,普通順序

	
		short ga, gu, gi; long foo, bar; double x, y, z[3]; char a, b; float baz;
		推薦的代碼,改進(jìn)的順序

	
		double z[3]; double x, y; long foo, bar; float baz; short ga, gu, gi;

(4)把頻繁使用的指針型參數(shù)拷貝到本地變量

避免在函數(shù)中頻繁使用指針型參數(shù)指向的值。因?yàn)榫幾g器不知道指針之間是否存在沖突,所以指針型參數(shù)往往不能被編譯器優(yōu)化。這樣數(shù)據(jù)不能被存放在寄存器中,而且明顯地占用了內(nèi)存帶寬。注意,很多編譯器有“假設(shè)不沖突”優(yōu)化開(kāi)關(guān)(在VC里必須手動(dòng)添加編譯器命令行/Oa或/Ow),這允許編譯器假設(shè)兩個(gè)不同的指針總是有不同的內(nèi)容,這樣就不用把指針型參數(shù)保存到本地變量。否則,請(qǐng)?jiān)诤瘮?shù)一開(kāi)始把指針指向的數(shù)據(jù)保存到本地變量。如果需要的話(huà),在函數(shù)結(jié)束前拷貝回去。 不好的代碼:

	
		// 假設(shè) q != r void isqrt(unsigned long a, unsigned long* q, unsigned long* r) { *q = a; if (a > 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)充分分解小的循環(huán)

要充分利用CPU的指令緩存,就要充分分解小的循環(huán)。特別是當(dāng)循環(huán)體本身很小的時(shí)候,分解循環(huán)可以提高性能。注意:很多編譯器并不能自動(dòng)分解循環(huán)。不好的代碼:

	
		// 3D轉(zhuǎn)化:把矢量 V 和 4x4 矩陣 M 相乘 for (i = 0;i < 4;i ++) {   r[i] = 0;   for (j = 0;j < 4;j ++)   {     r[i] += M[j][i]*V[j];   } }
		推薦的代碼:

	
		r[0] = M[0][0]*V[0] + M[1][0]*V[1] + M[2][0]*V[2] + M[3][0]*V[3]; r[1] = M[0][1]*V[0] + M[1][1]*V[1] + M[2][1]*V[2] + M[3][1]*V[3]; r[2] = M[0][2]*V[0] + M[1][2]*V[1] + M[2][2]*V[2] + M[3][2]*V[3]; r[3] = M[0][3]*V[0] + M[1][3]*V[1] + M[2][3]*V[2] + M[3][3]*v[3];

(2)提取公共部分

對(duì)于一些不需要循環(huán)變量參加運(yùn)算的任務(wù)可以把它們放到循環(huán)外面,這里的任務(wù)包括表達(dá)式、函數(shù)的調(diào)用、指針運(yùn)算、數(shù)組訪(fǎng)問(wèn)等,應(yīng)該將沒(méi)有必要執(zhí)行多次的操作全部集合在一起,放到一個(gè)init的初始化程序中進(jìn)行。

(3)延時(shí)函數(shù)

通常使用的延時(shí)函數(shù)均采用自加的形式:

	
		void delay (void) { unsigned int i; for (i=0;i<1000;i++) ; }
		將其改為自減延時(shí)函數(shù):

	
		void delay (void) { unsigned int i; for (i=1000;i>0;i--) ; }
		兩個(gè)函數(shù)的延時(shí)效果相似,但幾乎所有的C編譯對(duì)后一種函數(shù)生成的代碼均比前一種代碼少1~3個(gè)字節(jié),因?yàn)閹缀跛械?a  target="_blank">MCU均有為0轉(zhuǎn)移的指令,采用后一種方式能夠生成這類(lèi)指令。在使用while循環(huán)時(shí)也一樣,使用自減指令控制循環(huán)會(huì)比使用自加指令控制循環(huán)生成的代碼更少1~3個(gè)字母。但是在循環(huán)中有通過(guò)循環(huán)變量“i”讀寫(xiě)數(shù)組的指令時(shí),使用預(yù)減循環(huán)有可能使數(shù)組超界,要引起注意。

(4)while循環(huán)和do…while循環(huán)

用while循環(huán)時(shí)有以下兩種循環(huán)形式:

	
		unsigned int i; i=0; while (i<1000) { i++; //用戶(hù)程序 }
		或:

	
		unsigned int i; i=1000; do { i--; //用戶(hù)程序 } while (i>0);
		在這兩種循環(huán)中,使用do…while循環(huán)編譯后生成的代碼的長(zhǎng)度短于while循環(huán)。

(5)循環(huán)展開(kāi)

這是經(jīng)典的速度優(yōu)化,但許多編譯程序(如gcc -funroll-loops)能自動(dòng)完成這個(gè)事,所以現(xiàn)在你自己來(lái)優(yōu)化這個(gè)顯得效果不明顯。 舊代碼:

	
		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)時(shí)間節(jié)約了90%。不過(guò)注意:對(duì)于中間變量或結(jié)果被更改的循環(huán),編譯程序往往拒絕展開(kāi),(怕?lián)?zé)任唄),這時(shí)候就需要你自己來(lái)做展開(kāi)工作了。
		還有一點(diǎn)請(qǐng)注意,在有內(nèi)部指令cache的CPU上(如MMX芯片),因?yàn)檠h(huán)展開(kāi)的代碼很大,往往cache溢出,這時(shí)展開(kāi)的代碼會(huì)頻繁地在CPU 的cache和內(nèi)存之間調(diào)來(lái)調(diào)去,又因?yàn)閏ache速度很高,所以此時(shí)循環(huán)展開(kāi)反而會(huì)變慢。還有就是循環(huán)展開(kāi)會(huì)影響矢量運(yùn)算優(yōu)化。

(6)循環(huán)嵌套

把相關(guān)循環(huán)放到一個(gè)循環(huán)里,也會(huì)加快速度。 舊代碼:

	
		for (i = 0; i < MAX; i++) /* initialize 2d array to 0's */ for (j = 0; j < MAX; j++) a[i][j] = 0.0; for (i = 0; i < MAX; i++) /* put 1's along the diagonal */ a[i][i] = 1.0;
		新代碼:

	
		for (i = 0; i < MAX; i++) /* initialize 2d array to 0's */ { for (j = 0; j < MAX; j++) a[i][j] = 0.0; a[i][i] = 1.0; /* put 1's along the diagonal */ }

(7)Switch語(yǔ)句中根據(jù)發(fā)生頻率來(lái)進(jìn)行case排序

Switch 可能轉(zhuǎn)化成多種不同算法的代碼。其中最常見(jiàn)的是跳轉(zhuǎn)表和比較鏈/樹(shù)。當(dāng)switch用比較鏈的方式轉(zhuǎn)化時(shí),編譯器會(huì)產(chǎn)生if-else-if的嵌套代碼,并按照順序進(jìn)行比較,匹配時(shí)就跳轉(zhuǎn)到滿(mǎn)足條件的語(yǔ)句執(zhí)行。所以可以對(duì)case的值依照發(fā)生的可能性進(jìn)行排序,把最有可能的放在第一位,這樣可以提高性能。此外,在case中推薦使用小的連續(xù)的整數(shù),因?yàn)樵谶@種情況下,所有的編譯器都可以把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 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; default: cout << "month has fewer than 28 or more than 31 days" << endl;     break; }

(8)將大的switch語(yǔ)句轉(zhuǎn)為嵌套switch語(yǔ)句

當(dāng)switch語(yǔ)句中的case標(biāo)號(hào)很多時(shí),為了減少比較的次數(shù),明智的做法是把大switch語(yǔ)句轉(zhuǎn)為嵌套switch語(yǔ)句。把發(fā)生頻率高的case 標(biāo)號(hào)放在一個(gè)switch語(yǔ)句中,并且是嵌套switch語(yǔ)句的最外層,發(fā)生相對(duì)頻率相對(duì)低的case標(biāo)號(hào)放在另一個(gè)switch語(yǔ)句中。比如,下面的程序段把相對(duì)發(fā)生頻率低的情況放在缺省的case標(biāo)號(hào)內(nèi)。

	
		pMsg=ReceiveMessage(); switch (pMsg->type) { case FREQUENT_MSG1: handleFrequentMsg(); break; case FREQUENT_MSG2: handleFrequentMsg2(); break; 。。。。。。 case FREQUENT_MSGn: handleFrequentMsgn(); break; default: //嵌套部分用來(lái)處理不經(jīng)常發(fā)生的消息 switch (pMsg->type) { case INFREQUENT_MSG1: handleInfrequentMsg1(); break; case INFREQUENT_MSG2: handleInfrequentMsg2(); break; 。。。。。。 case INFREQUENT_MSGm: handleInfrequentMsgm(); break; } }
		如果switch中每一種情況下都有很多的工作要做,那么把整個(gè)switch語(yǔ)句用一個(gè)指向函數(shù)指針的表來(lái)替換會(huì)更加有效,比如下面的switch語(yǔ)句,有三種情況:

	
		enum MsgType{Msg1, Msg2, Msg3} switch (ReceiveMessage() { case Msg1; 。。。。。。 case Msg2; 。。。。。 case Msg3; 。。。。。 }
		為了提高執(zhí)行速度,用下面這段代碼來(lái)替換這個(gè)上面的switch語(yǔ)句。

	
		/*準(zhǔn)備工作*/ int handleMsg1(void); int handleMsg2(void); int handleMsg3(void); /*創(chuàng)建一個(gè)函數(shù)指針數(shù)組*/ int (*MsgFunction [])()={handleMsg1, handleMsg2, handleMsg3}; /*用下面這行更有效的代碼來(lái)替換switch語(yǔ)句*/ status=MsgFunction[ReceiveMessage()]();

(9)循環(huán)轉(zhuǎn)置

有些機(jī)器對(duì)JNZ(為0轉(zhuǎn)移)有特別的指令處理,速度非???,如果你的循環(huán)對(duì)方向不敏感,可以由大向小循環(huán)。 舊代碼:

	
		for (i = 1; i <= MAX; i++) { 。。。 }
		新代碼:

	
		i = MAX+1; while (--i) { 。。。 }
		不過(guò)千萬(wàn)注意,如果指針操作使用了i值,這種方法可能引起指針越界的嚴(yán)重錯(cuò)誤(i = MAX+1;)。當(dāng)然你可以通過(guò)對(duì)i做加減運(yùn)算來(lái)糾正,但是這樣就起不到加速的作用,除非類(lèi)似于以下情況:
		舊代碼:

	
		char a[MAX+5]; for (i = 1; i <= MAX; i++) { *(a+i+4)=0; }
		新代碼:

	
		i = MAX+1; while (--i) { *(a+i+4)=0; }

(10)公用代碼塊

一些公用處理模塊,為了滿(mǎn)足各種不同的調(diào)用需要,往往在內(nèi)部采用了大量的if-then-else結(jié)構(gòu),這樣很不好,判斷語(yǔ)句如果太復(fù)雜,會(huì)消耗大量的時(shí)間的,應(yīng)該盡量減少公用代碼塊的使用。(任何情況下,空間優(yōu)化和時(shí)間優(yōu)化都是對(duì)立的--東樓)。當(dāng)然,如果僅僅是一個(gè)(3==x)之類(lèi)的簡(jiǎn)單判斷,適當(dāng)使用一下,也還是允許的。記住,優(yōu)化永遠(yuǎn)是追求一種平衡,而不是走極端。

(11)提升循環(huán)的性能

要提升循環(huán)的性能,減少多余的常量計(jì)算非常有用(比如,不隨循環(huán)變化的計(jì)算)。 不好的代碼(在for()中包含不變的if()):

	
		for( i 。。。) { if( CONSTANT0 ) {  DoWork0( i );// 假設(shè)這里不改變CONSTANT0的值 } else { DoWork1( i );// 假設(shè)這里不改變CONSTANT0的值 } }
		推薦的代碼:

	
		if( CONSTANT0 ) { for( i 。。。) { DoWork0( i ); } } else { for( i 。。。) { DoWork1( i ); } }
		如果已經(jīng)知道if()的值,這樣可以避免重復(fù)計(jì)算。雖然不好的代碼中的分支可以簡(jiǎn)單地預(yù)測(cè),但是由于推薦的代碼在進(jìn)入循環(huán)前分支已經(jīng)確定,就可以減少對(duì)分支預(yù)測(cè)的依賴(lài)。

(12)選擇好的無(wú)限循環(huán)

編程中,我們常常需要用到無(wú)限循環(huán),常用的兩種方法是while (1)for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯后的代碼: 編譯前:

	
		while (1);
		編譯后:

	
		mov eax,1 test eax,eax je foo+23h jmp foo+18h
		編譯前:

	
		for (;;);
		編譯后:

	
		jmp foo+23h
		顯然,for (;;)指令少,不占用寄存器,而且沒(méi)有判斷、跳轉(zhuǎn),比while (1)好。

6、提高CPU的并行性

(1)使用并行代碼

盡可能把長(zhǎng)的有依賴(lài)的代碼鏈分解成幾個(gè)可以在流水線(xiàn)執(zhí)行單元中并行執(zhí)行的沒(méi)有依賴(lài)的代碼鏈。很多高級(jí)語(yǔ)言,包括C++,并不對(duì)產(chǎn)生的浮點(diǎn)表達(dá)式重新排序,因?yàn)槟鞘且粋€(gè)相當(dāng)復(fù)雜的過(guò)程。需要注意的是,重排序的代碼和原來(lái)的代碼在代碼上一致并不等價(jià)于計(jì)算結(jié)果一致,因?yàn)楦↑c(diǎn)操作缺乏精確度。在一些情況下,這些優(yōu)化可能導(dǎo)致意料之外的結(jié)果。幸運(yùn)的是,在大部分情況下,最后結(jié)果可能只有最不重要的位(即最低位)是錯(cuò)誤的。 不好的代碼:

	
		double a[100], sum; int i; sum = 0.0f; for (i=0;i<100;i++) sum += a[i];
		推薦的代碼:

	
		double a[100], sum1, sum2, sum3, sum4, sum; int i; sum1 = sum2 = sum3 = sum4 = 0.0; for (i = 0;i < 100;i += 4) {   sum1 += a[i];   sum2 += a[i+1];   sum3 += a[i+2];   sum4 += a[i+3]; } sum = (sum4+sum3)+(sum1+sum2);
		要注意的是:使用4路分解是因?yàn)檫@樣使用了4段流水線(xiàn)浮點(diǎn)加法,浮點(diǎn)加法的每一個(gè)段占用一個(gè)時(shí)鐘周期,保證了最大的資源利用率。

(2)避免沒(méi)有必要的讀寫(xiě)依賴(lài)

當(dāng)數(shù)據(jù)保存到內(nèi)存時(shí)存在讀寫(xiě)依賴(lài),即數(shù)據(jù)必須在正確寫(xiě)入后才能再次讀取。雖然AMD Athlon等CPU有加速讀寫(xiě)依賴(lài)延遲的硬件,允許在要保存的數(shù)據(jù)被寫(xiě)入內(nèi)存前讀取出來(lái),但是,如果避免了讀寫(xiě)依賴(lài)并把數(shù)據(jù)保存在內(nèi)部寄存器中,速度會(huì)更快。在一段很長(zhǎng)的又互相依賴(lài)的代碼鏈中,避免讀寫(xiě)依賴(lài)顯得尤其重要。如果讀寫(xiě)依賴(lài)發(fā)生在操作數(shù)組時(shí),許多編譯器不能自動(dòng)優(yōu)化代碼以避免讀寫(xiě)依賴(lài)。所以推薦程序員手動(dòng)去消除讀寫(xiě)依賴(lài),舉例來(lái)說(shuō),引進(jìn)一個(gè)可以保存在寄存器中的臨時(shí)變量。這樣可以有很大的性能提升。下面一段代碼是一個(gè)例子: 不好的代碼:

	
		float x[VECLEN], y[VECLEN], z[VECLEN]; 。。。。。。 for (unsigned int k = 1;k < VECLEN;k ++) {   x[k] = x[k-1] + y[k]; } for (k = 1;k 

	
		float x[VECLEN], y[VECLEN], z[VECLEN]; 。。。。。。 float t(x[0]); for (unsigned int k = 1;k < VECLEN;k ++) {   t = t + y[k];   x[k] = t; } t = x[0]; for (k = 1;k <;VECLEN;k ++) {   t = z[k] * (y[k] - t);   x[k] = t; }

7、循環(huán)不變計(jì)算

對(duì)于一些不需要循環(huán)變量參加運(yùn)算的計(jì)算任務(wù)可以把它們放到循環(huán)外面,現(xiàn)在許多編譯器還是能自己干這件事,不過(guò)對(duì)于中間使用了變量的算式它們就不敢動(dòng)了,所以很多情況下你還得自己干。對(duì)于那些在循環(huán)中調(diào)用的函數(shù),凡是沒(méi)必要執(zhí)行多次的操作通通提出來(lái),放到一個(gè)init函數(shù)里,循環(huán)前調(diào)用。另外盡量減少喂食次數(shù),沒(méi)必要的話(huà)盡量不給它傳參,需要循環(huán)變量的話(huà)讓它自己建立一個(gè)靜態(tài)循環(huán)變量自己累加,速度會(huì)快一點(diǎn)。 還有就是結(jié)構(gòu)體訪(fǎng)問(wèn),東樓的經(jīng)驗(yàn),凡是在循環(huán)里對(duì)一個(gè)結(jié)構(gòu)體的兩個(gè)以上的元素執(zhí)行了訪(fǎng)問(wèn),就有必要建立中間變量了(結(jié)構(gòu)這樣,那C++的對(duì)象呢?想想看),看下面的例子: 舊代碼:

	
		total = a->b->c[4]->aardvark + a->b->c[4]->baboon + a->b->c[4]->cheetah + a->b->c[4]->dog;
		新代碼:

	
		struct animals * temp = a->b->c[4]; total = temp->aardvark + temp->baboon + temp->cheetah + temp->dog;
		一些老的C語(yǔ)言編譯器不做聚合優(yōu)化,而符合ANSI規(guī)范的新的編譯器可以自動(dòng)完成這個(gè)優(yōu)化,看例子:

	
		float a, b, c, d, f, g; 。。。 a = b / c * d; f = b * g / c;
		這種寫(xiě)法當(dāng)然要得,但是沒(méi)有優(yōu)化

	
		float a, b, c, d, f, g; 。。。 a = b / c * d; f = b / c * g;
		如果這么寫(xiě)的話(huà),一個(gè)符合ANSI規(guī)范的新的編譯器可以只計(jì)算b/c一次,然后將結(jié)果代入第二個(gè)式子,節(jié)約了一次除法運(yùn)算。

8、函數(shù)優(yōu)化

(1)Inline函數(shù)

在C++中,關(guān)鍵字Inline可以被加入到任何函數(shù)的聲明中。這個(gè)關(guān)鍵字請(qǐng)求編譯器用函數(shù)內(nèi)部的代碼替換所有對(duì)于指出的函數(shù)的調(diào)用。這樣做在兩個(gè)方面快于函數(shù)調(diào)用:第一,省去了調(diào)用指令需要的執(zhí)行時(shí)間;第二,省去了傳遞變?cè)蛡鬟f過(guò)程需要的時(shí)間。但是使用這種方法在優(yōu)化程序速度的同時(shí),程序長(zhǎng)度變大了,因此需要更多的ROM。使用這種優(yōu)化在Inline函數(shù)頻繁調(diào)用并且只包含幾行代碼的時(shí)候是最有效的。

(2)不定義不使用的返回值

函數(shù)定義并不知道函數(shù)返回值是否被使用,假如返回值從來(lái)不會(huì)被用到,應(yīng)該使用void來(lái)明確聲明函數(shù)不返回任何值。

(3)減少函數(shù)調(diào)用參數(shù)

使用全局變量比函數(shù)傳遞參數(shù)更加有效率。這樣做去除了函數(shù)調(diào)用參數(shù)入棧和函數(shù)完成后參數(shù)出棧所需要的時(shí)間。然而決定使用全局變量會(huì)影響程序的模塊化和重入,故要慎重使用。

(4)所有函數(shù)都應(yīng)該有原型定義

一般來(lái)說(shuō),所有函數(shù)都應(yīng)該有原型定義。原型定義可以傳達(dá)給編譯器更多的可能用于優(yōu)化的信息。

(5)盡可能使用常量(const)

盡可能使用常量(const)。C++ 標(biāo)準(zhǔn)規(guī)定,如果一個(gè)const聲明的對(duì)象的地址不被獲取,允許編譯器不對(duì)它分配儲(chǔ)存空間。這樣可以使代碼更有效率,而且可以生成更好的代碼。

(6)把本地函數(shù)聲明為靜態(tài)的(static)

如果一個(gè)函數(shù)只在實(shí)現(xiàn)它的文件中被使用,把它聲明為靜態(tài)的(static)以強(qiáng)制使用內(nèi)部連接。否則,默認(rèn)的情況下會(huì)把函數(shù)定義為外部連接。這樣可能會(huì)影響某些編譯器的優(yōu)化——比如,自動(dòng)內(nèi)聯(lián)。

9、采用遞歸

與LISP之類(lèi)的語(yǔ)言不同,C語(yǔ)言一開(kāi)始就病態(tài)地喜歡用重復(fù)代碼循環(huán),許多C程序員都是除非算法要求,堅(jiān)決不用遞歸。事實(shí)上,C編譯器們對(duì)優(yōu)化遞歸調(diào)用一點(diǎn)都不反感,相反,它們還很喜歡干這件事。只有在遞歸函數(shù)需要傳遞大量參數(shù),可能造成瓶頸的時(shí)候,才應(yīng)該使用循環(huán)代碼,其他時(shí)候,還是用遞歸好些。

10、變量

(1)register變量

在聲明局部變量的時(shí)候可以使用register關(guān)鍵字。這就使得編譯器把變量放入一個(gè)多用途的寄存器中,而不是在堆棧中,合理使用這種方法可以提高執(zhí)行速度。函數(shù)調(diào)用越是頻繁,越是可能提高代碼的速度。 在最內(nèi)層循環(huán)避免使用全局變量和靜態(tài)變量,除非你能確定它在循環(huán)周期中不會(huì)動(dòng)態(tài)變化,大多數(shù)編譯器優(yōu)化變量都只有一個(gè)辦法,就是將他們置成寄存器變量,而對(duì)于動(dòng)態(tài)變量,它們干脆放棄對(duì)整個(gè)表達(dá)式的優(yōu)化。盡量避免把一個(gè)變量地址傳遞給另一個(gè)函數(shù),雖然這個(gè)還很常用。C語(yǔ)言的編譯器們總是先假定每一個(gè)函數(shù)的變量都是內(nèi)部變量,這是由它的機(jī)制決定的,在這種情況下,它們的優(yōu)化完成得最好。但是,一旦一個(gè)變量有可能被別的函數(shù)改變,這幫兄弟就再也不敢把變量放到寄存器里了,嚴(yán)重影響速度。看例子:

	
		a = b(); c(&d);
		因?yàn)閐的地址被c函數(shù)使用,有可能被改變,編譯器不敢把它長(zhǎng)時(shí)間的放在寄存器里,一旦運(yùn)行到c(&d),編譯器就把它放回內(nèi)存,如果在循環(huán)里,會(huì)造成N次頻繁的在內(nèi)存和寄存器之間讀寫(xiě)d的動(dòng)作,眾所周知,CPU在系統(tǒng)總線(xiàn)上的讀寫(xiě)速度慢得很。比如你的賽楊300,CPU主頻300,總線(xiàn)速度最多66M,為了一個(gè)總線(xiàn)讀,CPU可能要等4-5個(gè)周期,得。。得。。得。。想起來(lái)都打顫。

(2)同時(shí)聲明多個(gè)變量?jī)?yōu)于單獨(dú)聲明變量

(3)短變量名優(yōu)于長(zhǎng)變量名,應(yīng)盡量使變量名短一點(diǎn)

(4)在循環(huán)開(kāi)始前聲明變量

11、使用嵌套的if結(jié)構(gòu)

在if結(jié)構(gòu)中如果要判斷的并列條件較多,最好將它們拆分成多個(gè)if結(jié)構(gòu),然后嵌套在一起,這樣可以避免無(wú)謂的判斷。 說(shuō)明:上面的優(yōu)化方案由王全明收集整理。很多資料來(lái)源網(wǎng)上,出處不祥,在此對(duì)所有作者一并致謝! 該方案主要是考慮到在嵌入式開(kāi)發(fā)中對(duì)程序執(zhí)行速度的要求特別高,所以該方案主要是為了優(yōu)化程序的執(zhí)行速度。 注意:優(yōu)化是有側(cè)重點(diǎn)的,優(yōu)化是一門(mén)平衡的藝術(shù),它往往要以犧牲程序的可讀性或者增加代碼長(zhǎng)度為代價(jià)。 審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5177

    文章

    19992

    瀏覽量

    325075
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    183

    文章

    7634

    瀏覽量

    144073
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4923

    瀏覽量

    72371
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    41202

原文標(biāo)題:嵌入式代碼高效運(yùn)行指南

文章出處:【微信號(hào):嵌入式開(kāi)發(fā)愛(ài)好者,微信公眾號(hào):嵌入式開(kāi)發(fā)愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    如何調(diào)試嵌入式代碼?

    我們?cè)谶M(jìn)行嵌入式系統(tǒng)開(kāi)發(fā)調(diào)試時(shí),受限于嵌入式芯片資源和性能,一般采用遠(yuǎn)程調(diào)試。在調(diào)試嵌入式底層代碼時(shí),gdbserver 無(wú)法運(yùn)行,我們?cè)撛?/div>
    發(fā)表于 12-17 06:32

    Caché嵌入式代碼分享

    文章目錄第十章 Caché 嵌入式代碼嵌入式HTML`&html`標(biāo)記語(yǔ)法嵌入式JavaScript嵌入式SQL第十章 Caché
    發(fā)表于 12-17 07:19

    嵌入式軟件中,如何能使程序簡(jiǎn)潔清晰?如何能使程序高效運(yùn)行?

    嵌入式軟件中,如何能使程序簡(jiǎn)潔清晰?如何能使程序高效運(yùn)行?
    發(fā)表于 12-23 07:51

    如何提高嵌入式軟件單元測(cè)試效率

    在本指南中,您將學(xué)習(xí)如何通過(guò)在更短的時(shí)間內(nèi)運(yùn)行更多的測(cè)試來(lái)增加您的單元測(cè)試吞吐量。 這種效率的提高來(lái)自于使用虛擬平臺(tái)而不是物理硬件作為開(kāi)發(fā)平臺(tái)。 本指南對(duì)任何開(kāi)發(fā)或運(yùn)行
    發(fā)表于 08-28 06:31

    MiniARM嵌入式工業(yè)控制模塊選型指南

    MiniARM嵌入式工業(yè)控制模塊選型指南 MiniARM 嵌入式工控模塊以小巧的尺寸將先進(jìn)ARM 嵌入式硬件、MiniISA 擴(kuò)展I/O 技術(shù)、多種自主現(xiàn)場(chǎng)總線(xiàn)控制技術(shù)和文件管理技術(shù)
    發(fā)表于 03-27 15:46 ?24次下載

    調(diào)試嵌入式系統(tǒng)串行總線(xiàn)應(yīng)用指南

    的幾乎每個(gè)電子設(shè)備都是嵌入式系統(tǒng)。通過(guò)本指南,您將學(xué)習(xí)到怎樣使用泰克示波器強(qiáng)大的觸發(fā)、解碼和搜索功能,高效解決嵌入式系統(tǒng)設(shè)計(jì)問(wèn)題。
    發(fā)表于 03-31 14:48 ?206次下載
    調(diào)試<b class='flag-5'>嵌入式</b>系統(tǒng)串行總線(xiàn)應(yīng)用<b class='flag-5'>指南</b>

    TI嵌入式處理技術(shù)指南

    TI嵌入式處理技術(shù)指南
    發(fā)表于 10-27 10:02 ?6次下載
    TI<b class='flag-5'>嵌入式</b>處理技術(shù)<b class='flag-5'>指南</b>

    如何提高嵌入式應(yīng)用設(shè)計(jì)中的DSP代碼運(yùn)行性能

    。這種運(yùn)行方式在傳統(tǒng)嵌入式應(yīng)用設(shè)計(jì)中很有效,但在一些算法比較復(fù)雜、系統(tǒng)響應(yīng)要求快速、通訊任務(wù)比較繁重的嵌入式應(yīng)用中,這種運(yùn)行方式會(huì)導(dǎo)致控制系統(tǒng)響應(yīng)遲緩、通訊異常、I/O操作緩慢等問(wèn)題。
    的頭像 發(fā)表于 08-07 08:12 ?2922次閱讀
    如何提高<b class='flag-5'>嵌入式</b>應(yīng)用設(shè)計(jì)中的DSP<b class='flag-5'>代碼</b><b class='flag-5'>運(yùn)行</b>性能

    關(guān)于嵌入式代碼的致命漏洞

    輸入可以被制作成“包含代碼”,并且該程序可以被欺騙來(lái)執(zhí)行該代碼。代碼注入缺陷意味著黑客可以劫持現(xiàn)有進(jìn)程,并以與原始進(jìn)程相同的權(quán)限執(zhí)行任何他們喜歡的代碼。 在許多
    的頭像 發(fā)表于 01-06 15:13 ?2170次閱讀

    嵌入式代碼的致命安全漏洞

    輸入可以被制作成“包含代碼”,并且該程序可以被欺騙來(lái)執(zhí)行該代碼。代碼注入缺陷意味著黑客可以劫持現(xiàn)有進(jìn)程,并以與原始進(jìn)程相同的權(quán)限執(zhí)行任何他們喜歡的代碼。 在許多
    的頭像 發(fā)表于 01-15 15:07 ?2589次閱讀

    使用STM32L5的OTFDEC和ICACHE功能實(shí)時(shí)高效運(yùn)行片外Flash中的加密代碼

    使用STM32L5的OTFDEC和ICACHE功能實(shí)時(shí)高效運(yùn)行片外Flash中的加密代碼(嵌入式開(kāi)發(fā)應(yīng)用)-目標(biāo) :? 保護(hù)外置Flash中的代碼
    發(fā)表于 07-30 11:11 ?9次下載
    使用STM32L5的OTFDEC和ICACHE功能實(shí)時(shí)<b class='flag-5'>高效</b><b class='flag-5'>運(yùn)行</b>片外Flash中的加密<b class='flag-5'>代碼</b>

    嵌入式外中斷c語(yǔ)言代碼

    嵌入式外中斷c語(yǔ)言代碼(arm嵌入式開(kāi)發(fā)實(shí)例)-嵌入式外中斷c語(yǔ)言代碼,有需要的可以參考!
    發(fā)表于 07-30 11:29 ?5次下載
    <b class='flag-5'>嵌入式</b>外中斷c語(yǔ)言<b class='flag-5'>代碼</b>

    嵌入式開(kāi)發(fā)蜂鳴器代碼

    嵌入式開(kāi)發(fā)蜂鳴器代碼
    發(fā)表于 03-29 17:04 ?3次下載

    嵌入式目標(biāo)快速入門(mén)指南

    嵌入式目標(biāo)快速入門(mén)指南
    發(fā)表于 05-12 19:23 ?2次下載
    <b class='flag-5'>嵌入式</b>目標(biāo)快速入門(mén)<b class='flag-5'>指南</b>

    嵌入式目標(biāo)快速入門(mén)指南

    嵌入式目標(biāo)快速入門(mén)指南
    發(fā)表于 06-29 19:15 ?2次下載
    <b class='flag-5'>嵌入式</b>目標(biāo)快速入門(mén)<b class='flag-5'>指南</b>