C程式最佳化之路(二)

來源:互聯網
上載者:User
本文講述在編寫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的時候就是從-1到M-2了,就是這麼簡單。(如果你動態申請a的話,free(a)可不要free(pa)因為pa不是數組的頭地址)

III.我們需要鏈表嗎

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

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

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.