小夥伴們是否是跟我同樣,覺得以前的內存優化已經完成了?不,這纔剛剛開始……讓咱們一塊兒進入這無休止的循環吧!數組
switch語句一般用於如下狀況:緩存
調用幾個函數中的一個app
設置一個變量或返回值函數
執行幾個代碼片段中的一個oop
若是case表示是密集的,在使用switch語句的前兩種狀況中,可使用效率更高的查找表。好比下面的兩個實現彙編代碼轉換成字符串的例程:性能
char * Condition_String1(int condition) { switch(condition) { case 0: return "EQ"; case 1: return "NE"; case 2: return "CS"; case 3: return "CC"; case 4: return "MI"; case 5: return "PL"; case 6: return "VS"; case 7: return "VC"; case 8: return "HI"; case 9: return "LS"; case 10: return "GE"; case 11: return "LT"; case 12: return "GT"; case 13: return "LE"; case 14: return ""; default: return 0; } }
char * Condition_String2(int condition) { if((unsigned) condition >= 15) return 0; return "EQ\0NE\0CS\0CC\0MI\0PL\0VS\0VC\0HI\0LS\0GE\0LT\0GT\0LE\0\0" + 3 * condition; }
第一個例程須要240個字節,第二個只須要72個。測試
若是不加留意地編寫循環終止條件,就可能會給程序帶來明顯的負擔。咱們應該儘可能使用「倒數到零」的循環,使用簡單的循環終止條件。循環終止條件相對簡單,程序在執行的時候也會消耗相對少的時間。拿下面兩個計算n!的例子來講,第一個例子使用遞增循環,第二個使用遞減循環。優化
int fact1_func (int n) { int i, fact = 1; for (i = 1; i <= n; i++) fact *= i; return (fact); }
int fact2_func(int n) { int i, fact = 1; for (i = n; i != 0; i--) fact *= i; return (fact); }
結果是,第二個例子要比第一個快得多。spa
這是一個簡單而有效的概念,一般狀況下,咱們習慣把for循環寫成這樣:指針
for( i = 0; i < 10; i++){ ... }
i 值依次爲:0,1,2,3,4,5,6,7,8,9
在不在意循環計數器順序的狀況下,咱們能夠這樣:
for( i = 10; i--; ) { ... }
i 值依次爲: 9,8,7,6,5,4,3,2,1,0,並且循環要更快
這種方法是可行的,由於它是用更快的i--做爲測試條件的,也就是說「i是否爲非零數,若是是減一,而後繼續」。相對於原先的代碼,處理器不得不「把i減去10,結果是否爲非零數,若是是,增長i,而後繼續」,在緊密循環(tight loop)中,這會產生顯著的區別。
這種語法看起來有一點陌生,卻徹底合法。循環中的第三條語句是可選的(無限循環能夠寫成這樣for(;;)),下面的寫法也能夠取得一樣的效果:
for(i = 10; i; i--){}
或者:
for(i = 10; i != 0; i--){}
咱們惟一要當心的地方是要記住循環須要中止在0(若是循環是從50-80,這樣作就不行了),並且循環的計數器爲倒計數方式。
另外,咱們還能夠把計數器分配到寄存器上,能夠產生更爲有效的代碼。這種將循環計數器初始化成循環次數,而後遞減到零的方法,一樣適用於while和do語句。
混合循環/ Loop jamming 在可使用一個循環的場合,決不要使用兩個。可是若是你要在循環中進行大量的工做,超過處理器的指令緩衝區,在這種狀況下,使用兩個分開的循環可能會更快,由於有可能這兩個循環都被完整的保存在指令緩衝區裏了。
// 原先的代碼 for(i = 0; i < 100; i++){ stuff(); } for(i = 0; i < 100; i++){ morestuff(); } //更好的作法 for(i = 0; i < 100; i++){ stuff(); morestuff(); }
調用函數的時候,在性能上就會付出必定的代價。不光要改變程序指針,還要將那些正在使用的變量壓入堆棧,分配新的變量空間。爲了提升程序的效率,在程序的函數結構上,有不少工做能夠作。保證程序的可讀性的同時,還要儘可能控制程序的大小。
若是一個函數在一個循環中被頻繁調用,就能夠考慮將這個循環放在函數的裏面,這樣能夠免去重複調用函數的負擔,好比:
for(i = 0 ; i < 100 ; i++) { func(t,i); } void func(int w, d) { lots of stuff. }
能夠寫成:
func(t); void func(w) { for(i = 0; i < 100; i++) { //lots of stuff. } }
爲了提升效率,能夠將小的循環解開,不過這樣會增長代碼的尺寸。循環被拆開後,會下降循環計數器更新的次數,減小所執行的循環的分支數目。若是循環只重複幾回,那它徹底能夠被拆解開,這樣,由循環所帶來的額外開銷就會消失。
好比:
for(i = 0; i < 3; i++){ something(i); } //更高效的方式: something(0); something(1); something(2);
由於在每次的循環中,i 的值都會增長,而後檢查是否有效。編譯器常常會把這種簡單的循環解開,前提是這些循環的次數是固定的。對於這樣的循環:
for(i = 0; i < limit; i++) { ... }
就不可能被拆解,由於咱們不知道它循環的次數究竟是多少。不過,將這種類型的循環拆解開並非不可能的。
與簡單循環相比,下面的代碼的長度要長不少,然而具備高得多的效率。選擇8做爲分塊大小,只是用來演示,任何合適的長度都是可行的。例子中,循環的成立條件每八次才被檢驗一次,而不是每次都要檢驗。若是須要處理的數組的大小是肯定的,咱們就可使用數組的大小做爲分塊的大小(或者是可以整除數組長度的數值)。不過,分塊的大小跟系統的緩存大小有關。
#include<stdio.H> #define BLOCKSIZE (8) int main(void) { int i = 0; int limit = 33; /* could be anything */ int blocklimit; /* The limit may not be divisible by BLOCKSIZE, go as near as we can first, then tidy up. */ blocklimit = (limit / BLOCKSIZE) * BLOCKSIZE; /* unroll the loop in blocks of 8 */ while(i < blocklimit) { printf("process(%d)\n", i); printf("process(%d)\n", i+1); printf("process(%d)\n", i+2); printf("process(%d)\n", i+3); printf("process(%d)\n", i+4); printf("process(%d)\n", i+5); printf("process(%d)\n", i+6); printf("process(%d)\n", i+7); /* update the counter */ i += 8; } /* * There may be some left to do. * This could be done as a simple for() loop, * but a switch is faster (and more interesting) */ if( i < limit ) { /* Jump into the case at the place that will allow * us to finish off the appropriate number of items. */ switch( limit - i ) { case 7 : printf("process(%d)\n", i); i++; case 6 : printf("process(%d)\n", i); i++; case 5 : printf("process(%d)\n", i); i++; case 4 : printf("process(%d)\n", i); i++; case 3 : printf("process(%d)\n", i); i++; case 2 : printf("process(%d)\n", i); i++; case 1 : printf("process(%d)\n", i); } } return 0; }
通過惰性評估和二分分解煎熬,小編覺得本身已經逃出生天了,哪知這纔剛剛開始,小夥伴們,還請持續關注更新,更多幹貨和資料請直接聯繫我,也能夠加羣710520381,邀請碼:柳貓,歡迎你們共同討論