无止境的内存优化——停不下的循环
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
小伙伴們是不是跟我一樣,以為之前的內(nèi)存優(yōu)化已經(jīng)完成了?不,這才剛剛開(kāi)始……讓我們一起進(jìn)入這無(wú)休止的循環(huán)吧!
switch語(yǔ)句和查找表 / Switch statement vs. lookup tables
switch語(yǔ)句通常用于以下情況:
調(diào)用幾個(gè)函數(shù)中的一個(gè)
設(shè)置一個(gè)變量或返回值
執(zhí)行幾個(gè)代碼片斷中的一個(gè)
如果case表示是密集的,在使用switch語(yǔ)句的前兩種情況中,可以使用效率更高的查找表。比如下面的兩個(gè)實(shí)現(xiàn)匯編代碼轉(zhuǎn)換成字符串的例程:
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; }第一個(gè)例程需要240個(gè)字節(jié),第二個(gè)只需要72個(gè)。
循環(huán)終止 / Loop termination
如果不加留意地編寫(xiě)循環(huán)終止條件,就可能會(huì)給程序帶來(lái)明顯的負(fù)擔(dān)。我們應(yīng)該盡量使用“倒數(shù)到零”的循環(huán),使用簡(jiǎn)單的循環(huán)終止條件。循環(huán)終止條件相對(duì)簡(jiǎn)單,程序在執(zhí)行的時(shí)候也會(huì)消耗相對(duì)少的時(shí)間。拿下面兩個(gè)計(jì)算n!的例子來(lái)說(shuō),第一個(gè)例子使用遞增循環(huán),第二個(gè)使用遞減循環(huá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); }結(jié)果是,第二個(gè)例子要比第一個(gè)快得多。
更快的for()循環(huán) / Faster for() loops
這是一個(gè)簡(jiǎn)單而有效的概念,通常情況下,我們習(xí)慣把for循環(huán)寫(xiě)成這樣:
for( i = 0; i < 10; i++){ ... }i 值依次為:0,1,2,3,4,5,6,7,8,9
在不在乎循環(huán)計(jì)數(shù)器順序的情況下,我們可以這樣:
for( i = 10; i--; ) { ... }i 值依次為: 9,8,7,6,5,4,3,2,1,0,而且循環(huán)要更快
這種方法是可行的,因?yàn)樗怯酶斓膇--作為測(cè)試條件的,也就是說(shuō)“i是否為非零數(shù),如果是減一,然后繼續(xù)”。相對(duì)于原先的代碼,處理器不得不“把i減去10,結(jié)果是否為非零數(shù),如果是,增加i,然后繼續(xù)”,在緊密循環(huán)(tight loop)中,這會(huì)產(chǎn)生顯著的區(qū)別。
這種語(yǔ)法看起來(lái)有一點(diǎn)陌生,卻完全合法。循環(huán)中的第三條語(yǔ)句是可選的(無(wú)限循環(huán)可以寫(xiě)成這樣for(;;)),下面的寫(xiě)法也可以取得同樣的效果:
for(i = 10; i; i--){}或者:
for(i = 10; i != 0; i--){}我們唯一要小心的地方是要記住循環(huán)需要停止在0(如果循環(huán)是從50-80,這樣做就不行了),而且循環(huán)的計(jì)數(shù)器為倒計(jì)數(shù)方式。
另外,我們還可以把計(jì)數(shù)器分配到寄存器上,可以產(chǎn)生更為有效的代碼。這種將循環(huán)計(jì)數(shù)器初始化成循環(huán)次數(shù),然后遞減到零的方法,同樣適用于while和do語(yǔ)句。
混合循環(huán)/ Loop jamming 在可以使用一個(gè)循環(huán)的場(chǎng)合,決不要使用兩個(gè)。但是如果你要在循環(huán)中進(jìn)行大量的工作,超過(guò)處理器的指令緩沖區(qū),在這種情況下,使用兩個(gè)分開(kāi)的循環(huán)可能會(huì)更快,因?yàn)橛锌赡苓@兩個(gè)循環(huán)都被完整的保存在指令緩沖區(qū)里了。
// 原先的代碼 for(i = 0; i < 100; i++){stuff(); } for(i = 0; i < 100; i++){morestuff(); } //更好的做法 for(i = 0; i < 100; i++){stuff();morestuff(); }函數(shù)循環(huán) / Function Looping
調(diào)用函數(shù)的時(shí)候,在性能上就會(huì)付出一定的代價(jià)。不光要改變程序指針,還要將那些正在使用的變量壓入堆棧,分配新的變量空間。為了提高程序的效率,在程序的函數(shù)結(jié)構(gòu)上,有很多工作可以做。保證程序的可讀性的同時(shí),還要盡量控制程序的大小。
如果一個(gè)函數(shù)在一個(gè)循環(huán)中被頻繁調(diào)用,就可以考慮將這個(gè)循環(huán)放在函數(shù)的里面,這樣可以免去重復(fù)調(diào)用函數(shù)的負(fù)擔(dān),比如:
for(i = 0 ; i < 100 ; i++) { func(t,i); } void func(int w, d) { lots of stuff. }可以寫(xiě)成:
func(t); void func(w) { for(i = 0; i < 100; i++) { //lots of stuff. } }展開(kāi)循環(huán) / Loop unrolling
為了提高效率,可以將小的循環(huán)解開(kāi),不過(guò)這樣會(huì)增加代碼的尺寸。循環(huán)被拆開(kāi)后,會(huì)降低循環(huán)計(jì)數(shù)器更新的次數(shù),減少所執(zhí)行的循環(huán)的分支數(shù)目。如果循環(huán)只重復(fù)幾次,那它完全可以被拆解開(kāi),這樣,由循環(huán)所帶來(lái)的額外開(kāi)銷(xiāo)就會(huì)消失。
比如:
for(i = 0; i < 3; i++){ something(i); } //更高效的方式: something(0); something(1); something(2);因?yàn)樵诿看蔚难h(huán)中,i 的值都會(huì)增加,然后檢查是否有效。編譯器經(jīng)常會(huì)把這種簡(jiǎn)單的循環(huán)解開(kāi),前提是這些循環(huán)的次數(shù)是固定的。對(duì)于這樣的循環(huán):
for(i = 0; i < limit; i++) { ... }就不可能被拆解,因?yàn)槲覀儾恢浪h(huán)的次數(shù)到底是多少。不過(guò),將這種類(lèi)型的循環(huán)拆解開(kāi)并不是不可能的。
與簡(jiǎn)單循環(huán)相比,下面的代碼的長(zhǎng)度要長(zhǎng)很多,然而具有高得多的效率。選擇8作為分塊大小,只是用來(lái)演示,任何合適的長(zhǎng)度都是可行的。例子中,循環(huán)的成立條件每八次才被檢驗(yàn)一次,而不是每次都要檢驗(yàn)。如果需要處理的數(shù)組的大小是確定的,我們就可以使用數(shù)組的大小作為分塊的大小(或者是能夠整除數(shù)組長(zhǎng)度的數(shù)值)。不過(guò),分塊的大小跟系統(tǒng)的緩存大小有關(guān)。
#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; }經(jīng)過(guò)惰性評(píng)估和二分分解煎熬,小編以為自己已經(jīng)逃出生天了,哪知這才剛剛開(kāi)始,小伙伴們,還請(qǐng)持續(xù)關(guān)注更新,更多干貨和資料請(qǐng)直接聯(lián)系我,也可以加群710520381,邀請(qǐng)碼:柳貓,歡迎大家共同討論
轉(zhuǎn)載于:https://my.oschina.net/u/3875054/blog/1828206
總結(jié)
以上是生活随笔為你收集整理的无止境的内存优化——停不下的循环的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 小程序获取form_id 与 小程序获取
- 下一篇: hybrid开发调试记录