C程序優化之路(二)

 本文講述在編寫C程序代碼的常用優化辦法,分爲I/O篇,內存篇,算法篇,MMX彙編篇。

二.內存篇

       在上一篇中我們講述瞭如何優化文件的讀寫,這一篇則主要講述對內存操作的優化,主要有數組的尋址,指針鏈表等,還有一些實用技巧。

I.優化數組的尋址

       在編寫程序時,我們常常使用一個一維數組a[M×N]來模 擬二維數組a[N][M],這個時候訪問a[]一維數組的時候:我們經常是這樣寫a[j×M+i](對於a[j][i])。這樣寫當然是無可置疑的,但是 顯然每個尋址語句j×M+i都要進行一次乘法運算。現在再讓我們看看二維數值的尋址,說到這裏我們不得不深入到C編譯器在申請二維數組和一維數組的內部細 節上――實際在申請二位數組和一維數組,編譯器的處理是不一樣的,申請一個a[N][M]的數組要比申請一個a[M×N]的數組佔用的空間大!二維數組的 結構是分爲兩部分的:

① 是一個指針數組,存儲的是每一行的起始地址,這也就是爲什麼在a[N][M]中,a[j]是一個指針而不是a[j][0]數據的原因。

② 是真正的M×N的連續數據塊,這解釋了爲什麼一個二維數組可以象一維數組那樣尋址的原因。(即a[j][i]等同於(a[0])[j×M+i])

清楚了這些,我們就可以知道二維數組要比(模擬該二維數組的)一維數組尋址效率高。因爲a[j][i]的尋址僅僅是訪問指針數組得到j行的地址,然後再+i,是沒有乘法運算的!

    所以,在處理一維數組的時候,我們常常採用下面的優化辦法:(僞碼例子)

    int a[M*N];
    int *b=a;
    for(…)
    {
        b[…]=…;
        …………
        b[…]=…;
        b+=M;
    }

這個是遍歷訪問數組的一個優化例子,每次b+=M就使得b更新爲下一行的頭指針。當然如果你願意的話,可以自己定義一個數組指針來存儲每一行的起始地址。 然後按照二維數組的尋址辦法來處理一維數組。不過,在這裏我建議你乾脆就直接申請一個二維數組比較的好。下面是動態申請和釋放一個二維數組的C代碼。

int get_mem2Dint(int ***array2D, int rows, int columns)     //h.263源代碼
{
    int i;

    if((*array2D = (int**)calloc(rows, sizeof(int*))) == NULL) no_mem_exit(1);
    if(((*array2D)[0] = (int* )calloc(rows*columns,sizeof(int ))) == NULL) no_mem_exit(1);

for(i=1 ; i<rows ; i++)
        (*array2D)[i] =  (*array2D)[i-1] + columns  ;

return rows*columns*sizeof(int);
}

void free_mem2D(byte **array2D)
{
    if (array2D)
    {
        if (array2D[0])
            free (array2D[0]);
        else
            error ("free_mem2D: trying to free unused memory",100);

        free (array2D);
    }
    else
    {
        error ("free_mem2D: trying to free unused memory",100);
    }
}

順便說一下,如果你的數組尋址有一個偏移量的話,不要寫爲a[x+offset],而應該爲 b=a+offset,然後訪問b[x]。

不過,如果你不是處理對速度有特別要求的程序的話,這樣的優化也就不必要了。記住,如果編普通程序的話,可讀性和可移值性是第一位的。

II.從負數開始的數組

       在編程的時候,你是不是經常要處理邊界問題呢?在處理邊界問題的時候,經常下標是從負數開始的,通常我們的處理是將邊界處理分離出來,單獨用額外的代碼寫。那麼當你知道如何使用從負數開始的數組的時候,邊界處理就方便多了。下面是靜態使用一個從-1開始的數組:

int a[M];

int *pa=a+1;

現在如果你使用pa訪問a的時候就是從-1M2了,就是這麼簡單。(如果你動態申請a的話,freea)可不要freepa)因爲pa不是數組的頭地址)

III.我們需要鏈表嗎

       相信大家在學習《數據結構》的時候,對鏈表是相當熟悉了,所以我看有人在編寫一些耗時算法的時候,也採用了鏈表的形式。這樣編寫當然對內存的佔用(似乎)少了,可是速度呢?如果你測試:申請並遍歷10000個元素鏈表的時間與遍歷相同元素的數組的時間,你就會發現時間相差了百倍!(以前測試過一個算法,用鏈表是1分鐘,用數組是4秒鐘)。所以這裏我的建議是:在編寫耗時大的代碼時,儘可能不要採用鏈表!

       其實實際上採用鏈表並不能真正節省內存,在編寫很多算法的時候,我們是知道要佔用多少內存的(至少也知道個大概),那麼與其用鏈表一點點的消耗內存,不如用數組一步就把內存佔用。採用鏈表的形式一定是在元素比較少,或者該部分基本不耗時的情況下。

(我估計鏈表主要慢是慢在它是一步步申請內存的,如果能夠象數組一樣分配一個大內存塊的話,應該也不怎麼耗時,這個沒有具體測試過。僅僅是猜想 :P)

發佈了23 篇原創文章 · 獲贊 3 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章