C語言內存優化——繼續含淚總結

以前分析了基本數據類型的優化,如今開始涉及全局和局部變量的優化,話說這個東西我從沒想過還能這樣優化的喂!函數

全局變量 / Global variables

全局變量不會被分配在寄存器上,修改全局變量須要經過指針或者調用函數的方式間接進行。因此編譯器不會將全局變量存儲在寄存器中,那樣會帶來額外的、沒必要要的負擔和存儲空間。因此在比較關鍵的循環中,咱們要不使用全局變量。優化

若是一個函數要頻繁的使用全局變量,咱們可使用局部變量,做爲全局變量的拷貝,這樣就可使用寄存器了。條件是本函數調用的任何子函數不使用這些全局變量。spa

舉個例子:指針

int f(void);
int g(void);
int errs;
void test1(void)
{
    errs += f();
    errs += g();
}
void test2(void)
{
    int localerrs = errs;
    localerrs += f();
    localerrs += g();
    errs = localerrs;
}

能夠看到test1()中每次加法都須要讀取和存儲全局變量errs,而在test2()中,localerrs分配在寄存器上,只須要一條指令。code

使用別名 / Using Aliases

考慮下面的例子:生命週期

void func1( int *data )
{
    int i;
    for(i = 0; i < 10; i++)
        anyfunc(*data, i);
}

即便*data歷來沒有變化,編譯器殊不知道anyfunc()沒有修改它,因而程序每次用到它的時候,都要把它從內存中讀出來,可能它只是某些變量的別名,這些變量在程序的其餘部分被修改。若是可以肯定它不會被改變,咱們能夠這樣寫:內存

void func1( int *data )
{
    int i;
    int localdata;
    localdata = *data;
    for(i=0; i<10; i++)
        anyfunc(localdata, i);
}

這樣會給編譯器優化工做更多的選擇餘地。編譯器

活躍變量和泄漏 / Live variables and spilling

寄存器的數量在每一個處理器當中都是固定的,因此在程序的某個特定的位置,能夠保存在寄存器中的變量的數量是有限制的。有些編譯器支持「生命週期分割」(live-range splitting),也就是說在函數的不一樣部分,變量能夠被分配到不一樣的寄存器或者內存中。變量的生存範圍被定義成:起點是對該變量的一次空間分配,終點是在下次空間分配以前的最後一次使用之間。在這個範圍內,變量的值是合法的,是活的。在生存範圍以外,變量再也不被使用,是死的,它的寄存器能夠供其餘變量使用,這樣,編譯器就能夠安排更多的變量到寄存器當中。it

可分配到寄存器的變量須要的寄存器數量等於通過生命範圍重疊的變量的數目,若是這個數目超過可用的寄存器的數量,有些變量就必須被暫時的存儲到內存中。這種處理叫作「泄漏(spilling)」。 編譯器優先釋放最不頻繁使用的變量,將釋放的代價降到最低。能夠經過如下方式避免變量的「釋放」:編譯

限制活躍變量的最大數目:一般可使用簡單小巧的表達式,在函數內部不使用太多的變量。把大的函數分割成更加簡單的、更加小巧的多個函數,也可能會有所幫助。

使用關鍵字register修飾最常用的變量:告訴編譯器這個變量將會被常常用到,要求編譯器使用很是高的優先級將此變量分配到寄存器中。儘管如此,在某些狀況下,變量仍是可能被泄漏。

變量類型 / Variable Types

C編譯器支持基本的變量類型:char、short、int、long(signed、unsigned)、float、double。爲變量定義最恰當的類型,很是重要,由於這樣能夠減小代碼和數據的長度,能夠很是顯著的提升效率。

局部變量 / Local variables

若是可能,局部變量要避免使用char和short。對於char和short類型,編譯器在每次分配空間之後,都要將這種局部變量的尺寸減小到8位或16位。這對於符號變量來講稱爲符號擴展,對無符號變量稱爲無符號擴展。這種操做是經過將寄存器左移24或16位,而後再有符號(或無符號的)右移一樣的位數來實現的,須要兩條指令(無符號字節變量的無符號擴展須要一條指令)。

這些移位操做能夠經過使用int和unsigned int的局部變量來避免。這對於那些首先將數據調到局部變量而後利用局部變量進行運算的狀況尤爲重要。即便數據以8位或16位的形式輸入或輸出,把他們看成32位來處理還是有意義的。

咱們來考慮下面的三個例子函數:

int wordinc (int a)
{ 
    return a + 1;
}
short shortinc (short a)
{ 
    return a + 1;
}
char charinc (char a)
{ 
    return a + 1;
}

他們的運算結果是相同的,可是第一個代碼片段要比其餘片段運行的要快。

是否是學到了呢?C語言博大進深,諸君還得一塊兒加油啊……請持續關注更新,更多幹貨和資料請直接聯繫我,也能夠加羣710520381,邀請碼:柳貓,歡迎你們共同討論

相關文章
相關標籤/搜索