談到優(yōu)化,很多人都會(huì)直接想到匯編。難道優(yōu)化只能在匯編層次嗎?當(dāng)然不是,C++層次一樣可以作代碼優(yōu)化,其中有些常常是意想不到的。在C++層次進(jìn)行優(yōu)化,比在匯編層次優(yōu)化具有更好的移植性,應(yīng)該是優(yōu)化中的首選做法。
確定浮點(diǎn)型變量和表達(dá)式是 float 型
為了讓編譯器產(chǎn)生更好的代碼(比如說產(chǎn)生3DNow! 或SSE指令的代碼),必須確定浮點(diǎn)型變量和表達(dá)式是 float 型的。要特別注意的是,以 ";F"; 或 ";f"; 為后綴(比如:3.14f)的浮點(diǎn)常量才是 float 型,否則默認(rèn)是 double 型。為了避免 float 型參數(shù)自動(dòng)轉(zhuǎn)化為 double,請(qǐng)?jiān)诤瘮?shù)聲明時(shí)使用 float。
使用32位的數(shù)據(jù)類型
編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。盡量使用32位的數(shù)據(jù)類型,因?yàn)樗鼈儽?6位的數(shù)據(jù)甚至8位的數(shù)據(jù)更有效率。
明智使用有符號(hào)整型變量
在很多情況下,你需要考慮整型變量是有符號(hào)還是無符號(hào)類型的。比如,保存一個(gè)人的體重?cái)?shù)據(jù)時(shí)不可能出現(xiàn)負(fù)數(shù),所以不需要使用有符號(hào)類型。但是,如果是要保存溫度數(shù)據(jù),就必須使用到有符號(hào)的變量。
在許多地方,考慮是否使用有符號(hào)的變量是必要的。在一些情況下,有符號(hào)的運(yùn)算比較快;但在一些情況下卻相反。
比如:整型到浮點(diǎn)轉(zhuǎn)化時(shí),使用大于16位的有符號(hào)整型比較快。因?yàn)閤86構(gòu)架中提供了從有符號(hào)整型轉(zhuǎn)化到浮點(diǎn)型的指令,但沒有提供從無符號(hào)整型轉(zhuǎn)化到浮點(diǎn)的指令??纯淳幾g器產(chǎn)生的匯編代碼:
不好的代碼:
編譯前 編譯后
double x; mov [foo + 4], 0
unsigned int i; mov eax, i
x = i; mov [foo], eax
flid qword ptr [foo]
fstp qword ptr [x]
上面的代碼比較慢。不僅因?yàn)橹噶顢?shù)目比較多,而且由于指令不能配對(duì)造成的FLID指令被延遲執(zhí)行。用以下代碼代替:
推薦的代碼:
編譯前 編譯后
double x; fild dword ptr [i]
int i; fstp qword ptr [x]
x = i;
在整數(shù)運(yùn)算中計(jì)算商和余數(shù)時(shí),使用無符號(hào)類型比較快。以下這段典型的代碼是編譯器產(chǎn)生的32位整型數(shù)除以4的代碼:
不好的代碼 推薦的代碼
編譯前 編譯后
int i; mov eax, i
i = i / 4; cdq
and edx, 3
add eax, edx
sar eax, 2
mov i, eax
編譯前 編譯后
unsigned int i; shr i, 2
i = i / 4;
總結(jié):
無符號(hào)類型用于:
除法和余數(shù)
循環(huán)計(jì)數(shù)
數(shù)組下標(biāo)
有符號(hào)類型用于:
整型到浮點(diǎn)的轉(zhuǎn)化
while VS. for
在編程中,我們常常需要用到無限循環(huán),常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯后的代碼:
編譯前 編譯后
while (1); mov eax,1
test eax,eax
je foo+23h
jmp foo+18h
編譯前 編譯后
for (;;); jmp foo+23h
一目了然,for (;;)指令少,不占用寄存器,而且沒有判斷跳轉(zhuǎn),比while (1)好。
使用數(shù)組型代替指針型
使用指針會(huì)使編譯器很難優(yōu)化它。因?yàn)槿狈τ行У闹羔槾a優(yōu)化的方法,編譯器總是假設(shè)指針可以訪問內(nèi)存的任意地方,包括分配給其他變量的儲(chǔ)存空間。所以為了編譯器產(chǎn)生優(yōu)化得更好的代碼,要避免在不必要的地方使用指針。一個(gè)典型的例子是訪問存放在數(shù)組中的數(shù)據(jù)。C++ 允許使用操作符 [] 或指針來訪問數(shù)組,使用數(shù)組型代碼會(huì)讓優(yōu)化器減少產(chǎn)生不安全代碼的可能性。比如,x[0] 和x[2] 不可能是同一個(gè)內(nèi)存地址,但 *p 和 *q 可能。強(qiáng)烈建議使用數(shù)組型,因?yàn)檫@樣可能會(huì)有意料之外的性能提升。
不好的代碼 推薦的代碼
typedef struct
{
float x,y,z,w;
} VERTEX;
typedef struct
{
float m[4][4];
} MATRIX;
void XForm(float* res, const float* v, const float* m, int nNumVerts)
{
float dp;
int i;
const VERTEX* vv = (VERTEX *)v;
for (i = 0; i <; nNumVerts; i++)
{
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 x
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 y
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 z
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 w
vv ++; // 下一個(gè)矢量
m -= 16;
}
}
typedef struct
{
float x,y,z,w;
} VERTEX;
typedef struct
{
float m[4][4];
} MATRIX;
void XForm (float* res, const float* v, const float* m, int nNumVerts)
{
int i;
const VERTEX* vv = (VERTEX*)v;
const MATRIX* mm = (MATRIX*)m;
VERTEX* rr = (VERTEX*)res;
for (i = 0; i <; nNumVerts; i++)
{
rr->;x = vv->;x * mm->;m[0][0] + vv->;y * mm->;m[0][1]
+ vv->;z * mm->;m[0][2] + vv->;w * mm->;m[0][3];
rr->;y = vv->;x * mm->;m[1][0] + vv->;y * mm->;m[1][1]
+ vv->;z * mm->;m[1][2] + vv->;w * mm->;m[1][3];
rr->;z = vv->;x * mm->;m[2][0] + vv->;y * mm->;m[2][1]
+ vv->;z * mm->;m[2][2] + vv->;w * mm->;m[2][3];
rr->;w = vv->;x * mm->;m[3][0] + vv->;y * mm->;m[3][1]
+ vv->;z * mm->;m[3][2] + vv->;w * mm->;m[3][3];
}
}
確定浮點(diǎn)型變量和表達(dá)式是 float 型
為了讓編譯器產(chǎn)生更好的代碼(比如說產(chǎn)生3DNow! 或SSE指令的代碼),必須確定浮點(diǎn)型變量和表達(dá)式是 float 型的。要特別注意的是,以 ";F"; 或 ";f"; 為后綴(比如:3.14f)的浮點(diǎn)常量才是 float 型,否則默認(rèn)是 double 型。為了避免 float 型參數(shù)自動(dòng)轉(zhuǎn)化為 double,請(qǐng)?jiān)诤瘮?shù)聲明時(shí)使用 float。
使用32位的數(shù)據(jù)類型
編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。盡量使用32位的數(shù)據(jù)類型,因?yàn)樗鼈儽?6位的數(shù)據(jù)甚至8位的數(shù)據(jù)更有效率。
明智使用有符號(hào)整型變量
在很多情況下,你需要考慮整型變量是有符號(hào)還是無符號(hào)類型的。比如,保存一個(gè)人的體重?cái)?shù)據(jù)時(shí)不可能出現(xiàn)負(fù)數(shù),所以不需要使用有符號(hào)類型。但是,如果是要保存溫度數(shù)據(jù),就必須使用到有符號(hào)的變量。
在許多地方,考慮是否使用有符號(hào)的變量是必要的。在一些情況下,有符號(hào)的運(yùn)算比較快;但在一些情況下卻相反。
比如:整型到浮點(diǎn)轉(zhuǎn)化時(shí),使用大于16位的有符號(hào)整型比較快。因?yàn)閤86構(gòu)架中提供了從有符號(hào)整型轉(zhuǎn)化到浮點(diǎn)型的指令,但沒有提供從無符號(hào)整型轉(zhuǎn)化到浮點(diǎn)的指令??纯淳幾g器產(chǎn)生的匯編代碼:
不好的代碼:
編譯前 編譯后
double x; mov [foo + 4], 0
unsigned int i; mov eax, i
x = i; mov [foo], eax
flid qword ptr [foo]
fstp qword ptr [x]
上面的代碼比較慢。不僅因?yàn)橹噶顢?shù)目比較多,而且由于指令不能配對(duì)造成的FLID指令被延遲執(zhí)行。用以下代碼代替:
推薦的代碼:
編譯前 編譯后
double x; fild dword ptr [i]
int i; fstp qword ptr [x]
x = i;
在整數(shù)運(yùn)算中計(jì)算商和余數(shù)時(shí),使用無符號(hào)類型比較快。以下這段典型的代碼是編譯器產(chǎn)生的32位整型數(shù)除以4的代碼:
不好的代碼 推薦的代碼
編譯前 編譯后
int i; mov eax, i
i = i / 4; cdq
and edx, 3
add eax, edx
sar eax, 2
mov i, eax
編譯前 編譯后
unsigned int i; shr i, 2
i = i / 4;
總結(jié):
無符號(hào)類型用于:
除法和余數(shù)
循環(huán)計(jì)數(shù)
數(shù)組下標(biāo)
有符號(hào)類型用于:
整型到浮點(diǎn)的轉(zhuǎn)化
while VS. for
在編程中,我們常常需要用到無限循環(huán),常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯后的代碼:
編譯前 編譯后
while (1); mov eax,1
test eax,eax
je foo+23h
jmp foo+18h
編譯前 編譯后
for (;;); jmp foo+23h
一目了然,for (;;)指令少,不占用寄存器,而且沒有判斷跳轉(zhuǎn),比while (1)好。
使用數(shù)組型代替指針型
使用指針會(huì)使編譯器很難優(yōu)化它。因?yàn)槿狈τ行У闹羔槾a優(yōu)化的方法,編譯器總是假設(shè)指針可以訪問內(nèi)存的任意地方,包括分配給其他變量的儲(chǔ)存空間。所以為了編譯器產(chǎn)生優(yōu)化得更好的代碼,要避免在不必要的地方使用指針。一個(gè)典型的例子是訪問存放在數(shù)組中的數(shù)據(jù)。C++ 允許使用操作符 [] 或指針來訪問數(shù)組,使用數(shù)組型代碼會(huì)讓優(yōu)化器減少產(chǎn)生不安全代碼的可能性。比如,x[0] 和x[2] 不可能是同一個(gè)內(nèi)存地址,但 *p 和 *q 可能。強(qiáng)烈建議使用數(shù)組型,因?yàn)檫@樣可能會(huì)有意料之外的性能提升。
不好的代碼 推薦的代碼
typedef struct
{
float x,y,z,w;
} VERTEX;
typedef struct
{
float m[4][4];
} MATRIX;
void XForm(float* res, const float* v, const float* m, int nNumVerts)
{
float dp;
int i;
const VERTEX* vv = (VERTEX *)v;
for (i = 0; i <; nNumVerts; i++)
{
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 x
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 y
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 z
dp = vv->;x * *m ++;
dp += vv->;y * *m ++;
dp += vv->;z * *m ++;
dp += vv->;w * *m ++;
*res ++ = dp; // 寫入轉(zhuǎn)換了的 w
vv ++; // 下一個(gè)矢量
m -= 16;
}
}
typedef struct
{
float x,y,z,w;
} VERTEX;
typedef struct
{
float m[4][4];
} MATRIX;
void XForm (float* res, const float* v, const float* m, int nNumVerts)
{
int i;
const VERTEX* vv = (VERTEX*)v;
const MATRIX* mm = (MATRIX*)m;
VERTEX* rr = (VERTEX*)res;
for (i = 0; i <; nNumVerts; i++)
{
rr->;x = vv->;x * mm->;m[0][0] + vv->;y * mm->;m[0][1]
+ vv->;z * mm->;m[0][2] + vv->;w * mm->;m[0][3];
rr->;y = vv->;x * mm->;m[1][0] + vv->;y * mm->;m[1][1]
+ vv->;z * mm->;m[1][2] + vv->;w * mm->;m[1][3];
rr->;z = vv->;x * mm->;m[2][0] + vv->;y * mm->;m[2][1]
+ vv->;z * mm->;m[2][2] + vv->;w * mm->;m[2][3];
rr->;w = vv->;x * mm->;m[3][0] + vv->;y * mm->;m[3][1]
+ vv->;z * mm->;m[3][2] + vv->;w * mm->;m[3][3];
}
}