C++編譯器優化
1、volatile:
易變性:volatile告訴編譯器,某個變數是易變的,當編譯器遇到這個變數的時候,只能從變數的記憶體地址中讀取這個變數,不可以從快取、暫存器、或者其它任何地方讀取。 順序性:兩個包含volatile變數的指令,編譯後不可以亂序。注意是編譯後不亂序,但是在執行的過程中還是可能會亂序的,這點需要由其它機制來保證,例如memory- barriers。 不可優化性:volatile告訴編譯器,不要對這個變數進行各種激進的優化,甚至將變數直接消除,保證程式碼中的指令一定會被執行。
2、NRV(Named Return Value)優化:
函式返回一個類,例如下:
class X;
X bar()
{
X x1; // 處理 x1.. return x1;
}
編譯器實現:
// 函式實現
void bar(X& __result)// 加上一個額外引數
{
// 預留x1的記憶體空間 X x1; // 編譯器產生的預設建構函式的呼叫, x1.X::X(); // 處理 x1.. // 編譯器產生的拷貝操作 __result.X::X(x1); return;
}
// 函式呼叫
X x2;// 這裡只是預留記憶體,並未呼叫初始化函式
bar(x2);
NRV優化後:
void bar(X& __result)
{
// 呼叫__result的預設建構函式 __result.X::X(); // 處理__result return;
}
3、迴圈內變數優化:
void test2(char *s);
void test()
{
int i;
for (i = 0; i < 10; i ++) {
char buf[256]; test2(buf); //呼叫test2是為了讓編譯器認為buf有用,以免被優化掉
}
}
彙編程式碼:
movl$10, %ebx
subl$272, %esp#分配272位元組棧空間
leal-264(%ebp), %esi#取buf地址
.L2:
movl%esi, (%esp)#buf地址入棧
calltest2#呼叫test2
subl$1, %ebx
jne .L2#迴圈未結束則跳到L2
該函式中,buf不會每次迴圈都生成,而是迴圈外生成,迴圈內不斷的使用。
4、算數式優化
a*2被編譯成a+a;無符號數a/2被編譯成a>>1;有符號數a/2。
5、memset函式優化
memset函式常用來初始化大段記憶體,但對小資料來說memset能否保持足夠高效呢?
看這段程式:
編譯成彙編:
movl$0, -24(%ebp)#設定s1
movl$0, -20(%ebp)
movl$0, -16(%ebp)
movl$0, -12(%ebp)
calltest2#呼叫test2
leal-8216(%ebp), %edx#設定s2
xorl%eax, %eax
movl%edx, %edi
movl$2048, %ecx
rep stosl
movl%edx, (%esp)#呼叫test2
calltest2
movl%ebx, (%esp)#設定s3
movl$8193, 8(%esp)
movl$0, 4(%esp)
callmemset
movl%ebx, (%esp)#呼叫test2
calltest2
當資料長度比較小時(如s1是16位元組),memset被編譯成連續的賦值語句;當資料長度不大於8KB時(如s2),memset用串操作指令來實現;當資料長度大於8KB時(如s3),memset被編譯成函式呼叫。
串操作類指令:在記憶體一個儲存區域連續存放著若干個位元組(或字)資料,這樣一組資料稱為“資料串”(高階語言視為陣列)。若每個資料是一個位元組,稱“位元組串”;若是字,則稱“字串”。串操作指令可以用來實現記憶體區域的資料串操作。串操作指令每次只處理資料串中的一個數據,但與重複字首配合使用(重複字首+串操作指令),則可使操作重複進行(其執行過程相當於一個迴圈程式的執行,重複次數由暫存器CX決定)。