編寫高效的C程式與C代碼最佳化

來源:互聯網
上載者:User

來自:codingwu-部落格園

作者:archimedes

出處:http://www.cnblogs.com/archimedes/

本篇文章翻譯自:http://www.codeproject.com/Articles/6154/Writing-Efficient-C-and-C-Code-Optimization


其中參考了文章:http://www.cppblog.com/xlander/archive/2006/07/21/10289.html


       但文章中的代碼格式沒有排版,不方便查看,而且有部分翻譯錯誤以及其他錯誤,這篇文章除了參考原文和譯文,也加入了自己的一些理解和代碼,雖然是一篇2006年的文章,但是其中的一些技巧還是挺值得學習的,特重新整理出來與大家分享。


      雖然對於最佳化C代碼有很多有效指導方針,但是對於徹底地瞭解編譯器和你工作的機器依然無法取代,通常,加快程式的速度也會加大代碼量。這些增加的代碼也會影響一個程式的複雜度和可讀性,這是不可接受的,比如你在一些小型的裝置上編程,例如:行動裝置、PDA……,這些有著嚴格的記憶體限制,於是,在最佳化的座右銘是:寫代碼在記憶體和速度都應該最佳化。


整型數 / Integers


在我們知道使用的數不可能是負數的時候,應該使用unsigned int取代int,一些處理器處理整數算數運算的時候unsigned int比int快,於是,在一個緊緻的迴圈裡面定義一個整型變數,最好這樣寫代碼:

register unsigned int variable_name;

然而,我們不能保證編譯器會注意到那個register關鍵字,也有可能,對某種處理器來說,有沒有unsigned是一樣的。這兩個關鍵字並不是可以在所有的編譯器中應用。記住,整形數運算要比浮點數運算快得多,因為處理器可以直接進行整型數運算,浮點數運算需要依賴於外部的浮點數處理器或者浮點數數學庫。我們處理小數的時候要精確點些(比如我們在做一個簡單的統計程式時),要限制結果不能超過100,要儘可能晚的把它轉化成浮點數。


除法和餘數 / Division and Remainder


在標準的處理器中,根據分子和分母的不同,一個32位的除法需要20-140個刻度來執行完成,等於一個固定的時間加上每個位被除的時間。

Time (分子/ 分母) = C0 + C1* log2 (分子/分母)

= C0 + C1 * (log2 (分子) - log2 (分母)).

現在的ARM處理器需要消耗20+4.3N個刻度,這是一個非常費時的操作,要儘可能的避免。在有些情況下,除法運算式可以用乘法表達是來重寫。比方說,(a/b)>c可以寫成a>(c*b),條件是我們已經知道b為非負數而且b*c不會超過整型數的取值範圍。如果我們能夠確定其中的一個運算元為unsigned,那麼使用無符號除法將會更好,因為它要比有符號除法快得多。


合并除法運算和取餘運算 / Combining division and remainder


在一些情況下,除法運算和取餘運算都需要用到,在這種情況下,編譯器會將除法運算和取餘運算合并,因為除法運算總是同時返回商和餘數。如果兩個運算都要用到,我們可以將他們寫到一起

int func_div_and_mod (int a, int b) {      return (a / b) + (a % b);}

除數是2的冪的除法和取餘 / Division and remainder by powers of two


如果除法運算中的除數是2的冪,我們對這個除法運算還可以進一步最佳化,編譯器會使用移位元運算來進行這種除法運算。所以,我們要儘可能調整比例為2的冪(比方說要用64而不用66)。如果是無符號數,它要比有符號的除法快得多。

typedef unsigned int uint;

uint div32u (uint a) {

return a / 32;

}

int div32s (int a) {

return a / 32;

}

這兩種除法都會避免調用除法函數,另外,無符號的除法要比有符號的除法使用更少的指令。有符號的除法要耗費更多的時間,因為這種除法是使最終結果趨向於零的,而移位則是趨向於負無窮。


模數運算的替換 / An alternative for modulo arithmetic


我們一般使用取餘運算進行模數,不過,有時候使用 if 語句來重寫也是可行的。考慮下面的兩個例子:

uint modulo_func1 (uint count)

{

return (++count % 60);

}

uint modulo_func2 (uint count)

{

if (++count >= 60)

count = 0;

return (count);

}

第二個例子要比第一個更可取,因為由它產生的代碼會更快,注意:這隻是在count取值範圍在0 – 59之間的時候才行。


但是我們可以使用如下的代碼(筆者補充)實現等價的功能:


uint modulo_func3 (uint count)

{

if (++count >= 60)

count %= 60;

return (count);

}

使用數組索引 / Using array indices


假設你要依據某個變數的值,設定另一個變數的取值為特定的字元,你可能會這樣做:

switch(queue) {

case 0 : letter = 'W';

break;

case 1 : letter = 'S';

break;

case 2 : letter = 'U';

break;

}

或者這樣:

if(queue == 0)

letter = 'W';

else if ( queue == 1 )

letter = 'S';

else

letter = 'U';

有一個簡潔且快速的方式是簡單的將變數的取值做成一個字串索引,例如:

static char *classes = "WSU";

letter = classes[queue];

全域變數 / Global variables


全域變數不會被分配在寄存器上,修改全域變數需要通過指標或者調用函數的方式間接進行。所以編譯器不會將全域變數儲存在寄存器中,那樣會帶來額外的、不必要的負擔和儲存空間。所以在比較關鍵的迴圈中,我們要不使用全域變數。


如果一個函數要頻繁的使用全域變數,我們可以使用局部變數,作為全域變數的拷貝,這樣就可以使用寄存器了。條件是本函數調用的任何子函數不使用這些全域變數。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.