通過設計合理的數據結構和算法將一些本需要在運行期間計算的信息預先存放在內存中來提升性能,是一種空間換時間的優化,下面一些實際的例子描述了這種優化方法的使用:
- 在一個遞增的數組中查詢和待查找元素最接近的的索引
例如數組[1,2,3,4,5],待查找元素為1.1返回數組索引0,待查找元素為1.6返回數組索引1,通過計算和數組每個元素的差值,循環遍歷一遍數組可以即可得到索引值,如下代碼所示:

const double g_searchTable[] = {1,2,3,4,5}; int getIndex(double data) { int index = 0; double temp; double temp1 = fabs(g_searchTable[0]-data); for(int i=0;i<sizeof(g_searchTable)/sizeof(g_searchTable[0]);i++) { temp = fabs(g_searchTable[i]-data); if(temp < temp1) { temp1 = temp; index = i; } } return index; }
該代碼在循環內部存在浮點數計算、計算絕對值而且要遍歷數組,如果數組足夠大且頻繁調用程序性能必然低下。我們可以通過把非遞減的數組元素看成一個連續區域中的幾個位置,計算出各個位置間的中點,划分為不同的區間,即可通過比較待待查找的元素落在什么區間中得出索引值。入下圖所示如果待查找元素小於1.5返回索引0。這里就需要我們增加一個區間數組[1.5,2.5,3.5,4.5],在代碼中直接跟元素比較即可得到索引信息。

const double g_searchTable[] = {1,2,3,4,5}; const double g_searchDivTable[] = {1.5,2.5,3.5,4.5}; int getIndex1(double data) { int index = 0; for(int i=0;i<sizeof(g_searchDivTable)/sizeof(g_searchDivTable[0]);i++) { if(data < g_searchDivTable[i]) { return i; } } return sizeof(g_searchDivTable)/sizeof(g_searchDivTable[0]); }
通過增加一個對應的區間數組空間,我們消除了浮點運算、絕對值計算,並且在確定待查元素的區間后即可得到結果,不用繼續遍歷數組。
- switch語句的編譯優化
我們在代碼中經常使用switch語句,其結構清晰,通過計算表達式的值跳轉到指定的case分支中,在編譯過程中往往並不是將表達式同case常量依次比較來定位到具體分支中,這樣的效率往往很低下,編譯器通常使用了“跳轉表技術”來定位分支目標地址。跳轉表可以看成一個地址數組,其中每一個元素表示一個代碼段的地址,通過映射表達式值為跳轉表中的索引即可獲取對應的目標地址。如下switch語句:
switch(n) { case 5: //ADDR1 case 7: //ADDR2 deafult: //DEFAULT_ADDR }
編譯器往往轉換為如下跳轉表:
JumpTable = [ADDR1, DEFAULT_ADDR, ADDR1]; int index = n-5; if index >2 goto DEFAULT_ADDR goto JumpTable [index]
通過使用跳轉表技術,增加跳轉表的空間消耗,可以直接定位到目標地址。
- 字符串到系統標示符的映射
在一些系統中我們往往需要將一系列的字符串轉換為系統對應的標示符,例如編譯器的分詞模塊對輸入的“int”字符串轉換為系統的TK_INT類型。系統中默認的標示符往往很多,如果對輸入字符串通過代碼分支依次比較將十分耗費時間。為了提升效率我們可以考慮通過使用hash技術,選取合適的散列函數將字符串轉換為一個映射數組的下標。下面代碼將“add”、“sub”、“mul”、“div”轉換為系統的對應類型:

typedef enum { T_ADD, T_SUB, T_MUL, T_DIV, T_ERR }Token; typedef struct { char* str; Token tk; char nextFlag; }MAP_T; MAP_T map0[] = {{"add",T_ADD,1},{"sub",T_SUB,1},{"mul",T_MUL,0}};//“add”、“sub”、“mul”通過散列函數計算出相同的數組下標0 MAP_T map1[] = {{"div",T_DIV,0}};//“div”通過散列函數計算出數組下標1 //映射信息 MAP_T* hashTable[] = {map0,map1}; Token getTk(char* str) { int index = -1; MAP_T *pMap = NULL; index = getHashCode(str); pMap = hashTable[index]; while(1) { if(0 == strcmp(str,pMap->str)) { return pMap->tk; } if(0 == pMap->nextFlag) { break; } pMap++; } return T_ERR; }
入上所示,我們保存了所有字符串和系統標示符的對應關系,計算待轉換字符串的hash值即可快速定位到一個小的區間中。通過選取適當散列函數可以的得到更好的效率。這里使用了鏈地址法來解決hash沖突。
以上實例都是基於空間換時間的優化方式,通過設計合理的數據結構,保存關鍵的信息,可以極大的對程序性能優化。