无止境的内存优化——停不下的循环
2019獨角獸企業重金招聘Python工程師標準>>>
小伙伴們是不是跟我一樣,以為之前的內存優化已經完成了?不,這才剛剛開始……讓我們一起進入這無休止的循環吧!
switch語句和查找表 / Switch statement vs. lookup tables
switch語句通常用于以下情況:
調用幾個函數中的一個
設置一個變量或返回值
執行幾個代碼片斷中的一個
如果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個。
循環終止 / Loop termination
如果不加留意地編寫循環終止條件,就可能會給程序帶來明顯的負擔。我們應該盡量使用“倒數到零”的循環,使用簡單的循環終止條件。循環終止條件相對簡單,程序在執行的時候也會消耗相對少的時間。拿下面兩個計算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); }結果是,第二個例子要比第一個快得多。
更快的for()循環 / Faster for() loops
這是一個簡單而有效的概念,通常情況下,我們習慣把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(); }函數循環 / Function Looping
調用函數的時候,在性能上就會付出一定的代價。不光要改變程序指針,還要將那些正在使用的變量壓入堆棧,分配新的變量空間。為了提高程序的效率,在程序的函數結構上,有很多工作可以做。保證程序的可讀性的同時,還要盡量控制程序的大小。
如果一個函數在一個循環中被頻繁調用,就可以考慮將這個循環放在函數的里面,這樣可以免去重復調用函數的負擔,比如:
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. } }展開循環 / Loop unrolling
為了提高效率,可以將小的循環解開,不過這樣會增加代碼的尺寸。循環被拆開后,會降低循環計數器更新的次數,減少所執行的循環的分支數目。如果循環只重復幾次,那它完全可以被拆解開,這樣,由循環所帶來的額外開銷就會消失。
比如:
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,邀請碼:柳貓,歡迎大家共同討論
轉載于:https://my.oschina.net/u/3875054/blog/1828206
總結
以上是生活随笔為你收集整理的无止境的内存优化——停不下的循环的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小程序获取form_id 与 小程序获取
- 下一篇: hybrid开发调试记录