C語言基本功教程系列 快速的函數調用

來源:互聯網
上載者:User
函數哪裡都有,小的程式一兩個函數,大的程式成百上千個函數。即使在遊戲的關鍵迴圈中,調用幾十個函數也是很常見的。所以函數調用代碼的品質,在很大程度上影響著遊戲的品質。

還是先說最基本的代碼風格問題。首先,對於函數的參數(特別是指標),如果函數內部不會修改其指標的內容,一定要用const來定義參數類型
=========不好的風格==========
void function(char * ServerName)
{
   // 內部不允許對ServerName的內容進行修改
}

=========好的風格===========
void function(const char * ServerName)
{
   // 內部不允許對ServerName的內容進行修改
}

為什麼這麼做呢? 舉個簡單的例子: 在團隊開發中程式員A寫好了displayFunction,傳了一個資料結構給displayFunction做圖象顯示,然後在接下來的程式中對資料進行計算。A認為displayFunction不會對資料進行修改,所以在以後的資料運算中,沒有進行一致性檢測。過了幾天程式員B被派過來最佳化A的程式,因為不知道不能改資料,結果改了下,在displayFunction中改變了資料結構的內容,當時測試通過。但是在產品發布的Alpha測試階段,用real data的時候出了問題。我想通宵debug去差這麼點個小問題,不是很值得吧。只要稍微留點心,就可以避免了

==================分割線==================
下面談談函數的調用問題。我們都知道,在調用的一個函數的時候,傳給函數的參數是要壓到棧裡,然後才能被函數訪問。我們來看一下函數調用的彙編代碼.(彙編代碼是用 Visual Studio .net 2003 編譯, release version。最佳化參數 /0t /02)

=======printf("%s%d%d%d%d",haha,m,n,p,i);======
00401000 push ecx
00401001 push ebx
00401002 mov ebx, dword ptr [esp+04]
00401003 push ebp
00401004 mov ebp, dword ptr [esp+08]
00401005 push esi
00401006 push edi
00401007 mov edi, dword ptr [esp+10] 
00401008 xor esi, esi
00401009 push esi
0040100A push edi
0040100B push ebx
0040100C push ebp
0040100D push 00408040
0040100E push 004060FC
0040100F call 00401054

我的天哪,這是多少代碼,只不過為了把參數push到棧裡就用了15條。看我們看看另一段代碼

===========printf("%s",haha);============
00401010 push 00408040
00401011 push 004060FC
00401012 call 00401054

現在我不用說大家都明白了吧。傳遞給函數的參數越少越好,最好就是一個指標,指向一個structure。這就是為什麼大部分的directX的函數就是一個指標的大structure傳過去。裡邊的參數好幾十個。當然了 void fucntion(void)是最快的函數調用,也可以用inline來最佳化關鍵迴圈內的函數。不過在每一個frame的執行代碼中,有成百上千個函數,不可能所有的都inline吧。所有能快點就快點嘍。當然了,傳遞 structure的reference也是同樣的效果,只要不把structure當參數就好。
============錯誤的方式===========
void function(struct OneStructure Parameter);
============正確的方式===========
void function(struct OneStructure & Parameter);
or
void function(struct OneStructure * pParameter);

==================分割線==================
這個例子不是很好,因為降低了代碼的可讀性,不過做為參考。。。。
很多人喜歡寫代碼的時候這麼寫:
char szName[] = "Aear";
int length;

length = strlen(szName);
if(length > 0)   // 這行的效率不考慮
{
   // do something
}
粗一看沒什麼問題,不過如果length在以後用不到的話,那麼就浪費了。因為length佔用了記憶體,而且浪費了cpu資源。讓我們看帶彙編代碼(彙編代碼是用Visual Studio .net 2003 編譯, release version。最佳化參數 /0t /02)

length = strlen(szName);
if(length > 0)   {...}

0040101F       sub eax, edx
00401021       mov dword ptr [esp+4], eax // 把傳回值存到length中
00401025       je 00401039                       // 判斷跳轉

========更快速的寫法的代碼========
if(strlen(szName)) {...}

0040101F       sub eax, edx
00401021       mov esi, eax   //把傳回值放在個臨時寄存器中
00401023       je 00401037

大家都知道寄存器之間進行資料操作是非常快的,而且是穩定的一個cpu clock cycle,至於 00401021       mov dword ptr [esp+4], eax 到底要花多少個clock cycle,那隻有天知道了。因為這種從記憶體中讀資料的指令,最少也是2個clock cycle,即使在L2 cache中,也不會比 mov esi, eax 快,而且浪費了棧空間。

==================再分割下吧,雖然不是很喜歡==================
最後說說一種類告訴的分枝判斷參數傳遞。在有些情況下,我們經常要傳很多參數,比如pixel shader等等,這些函數根據參數的設定,進行不同的操作。舉個例子:

struct Parameter{
     bool bDrawWater;
     bool bDrawSkybox;
     bool bDrawTerrain;
     bool bDrawSepcialEffects;
} DrawParamter;

void DrawEnvironment( struct Parameter * pPara)
{
     if(pPara->bDrawWater) {....};
     if(pPara->bDrawSkybox) {....};
     if(pPara->bDrawTerrain) {....};
     if(pPara->bDrawSpecialEffects) {....};
}

對於這樣的代碼,還有更快速, 更節省記憶體的方法,那就是位操作。
const static UINT32 DRAW_WATER_FLAG               = 1;
const static UINT32 DRAW_SKYBOX_FLAG              = 1 << 1;
const static UINT32 DRAW_TERRAIN_FLAG             = 1 << 2;
const static UINT32 DRAW_SPECIALEFFECTS_FLAG  = 1 << 3;

void DrawEnvironment(UINT32 DrawFlag)
{
    //注意了,這裡不需要 pPara->,也就是節省了記憶體訪問,速度至少提高了1到2個clock cycle
     if( DrawFlag & DRAW_WATER_FLAG ) {.....};
     if( DrawFlag & DRAW_SKYBOX_FLAG) {.....};

    //甚至還可以進行各種不同組合的判斷,比如
     if( DrawFlag & (DRAW_WATER_FLAG | DRAW_SKYBOX_FLAG) ) {....};
}

在調用的時候,代碼更加簡潔明了:
DrawEnvironment( DRAW_WATER_FLAG | DRAW_TERRAIN_FLAG );

 

聯繫我們

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