2012-02-04 24 views
5

昨天我接受了採訪。那裏他們問我代碼優化何時發生? 說,代碼優化何時發生?

int abc;//Global variable 
abc = 3; 
if(abc == 3) 
{ 
    printf("abc will be always 3"); 
} 
else 
{ 
    printf("This will never executed"); 
} 

現在的問題是,當發生了優化? A ...在運行時間 B ...在編譯時。 我在編譯時回答...對我來說,我認爲編譯器會在編譯時檢查volatile關鍵字。如果變量沒有被聲明爲volatile,那麼它會優化代碼。但是,當編譯器知道時,這個變量永遠不會不是3? 如果它在運行時,那麼當編譯器知道變量永遠不會是3以外的時候?因爲如果在這部分代碼執行後變量將被改變。請清除我的疑問

回答

4

大多數代碼優化不能在運行時發生的,至少沒怎麼你的意思。代碼在執行期間不能改變,以反映一組新的或不同的變量,這隻會造成絕對的混亂。

在運行時可以做些什麼是通過代碼選擇最佳路徑,但這主要是手動完成的,以便創建單獨的路徑,提前出局,分支等以實現優化。

像這樣寫的代碼可以編譯時優化,因爲編譯器可以檢查任何可能的替代值abc,如果找不到,可以優化調用。搜索的範圍會極大地影響是否會發生,然而,編譯器設置會影響這一點。

如果編譯器只是簡單地優化單個目標文件,而你的第二行是來自打印部分的另一個文件,那麼它可能無法保證abc不會改變,所以不能夠進行優化這一切。無論使用何種變量,它都取決於編譯器設置的激進程度以及它們是否被允許放棄無用分支,或者會考慮這樣做。優化尺寸可能更容易將分支移除而不是速度,但中/高設置可能會以任何方式進行(如果可能)。

大多數現代編譯器都有一個全程序優化選項,它延遲了大部分優化,直到鏈接器階段。這允許它搜索整個程序,可能發現abc從未在其他地方更改或使用過,並從條件中移除變量和失敗的分支。對於每個對象,整個程序優化可能比單獨優化更有效,因爲它可以實現更精確的搜索。

在編譯器無法修剪死代碼的情況下,您可以提示它(最近的構造如constexpr可以幫助您)或自己添加優化。這可以像將最可能的路徑放在第一位並在其他位置包括返回一樣簡單,從而使CPU不會跳躍。這種微型優化不太可能是必要的,當然不是這樣的簡單例子,其中if本身就是很多優化。

8

C代碼通常是使用靜態編譯(也稱爲提前編譯)編譯的。代碼一旦離開編譯器就會立即生效;它在運行時不能改變。

這與使用just-in-time compilation的語言(如Java)形成對比。在那裏,優化可以在程序運行的任何時候發生。

+1

在C中最接近運行時優化的是基於Profiler的優化。您編譯該程序,然後運行它,同時使用分析器收集統計信息。然後再次編譯,將這些統計數據提供給編譯器。第二個編譯將使用這些信息來生成更快的代碼。 – ugoren 2012-02-04 17:00:18

1

我並不太熟悉編譯器如何優化代碼,但是我知道從您在那裏的代碼中,編譯器可以推斷出abc永遠不會被更改。這意味着它可以可能完全取出if聲明,只是調用第一個printf函數。

然後在運行時,沒有太多優化,因爲代碼非常簡單。如果abc發生變化,那麼顯然if不能被消除,然而,然後,在運行時,有一些事情像CPU上的分支預測,將嘗試預測正在採取的代碼路徑。這也可以被認爲是一種優化形式。

注意:我沒有聲稱會發生這種情況,但我可以看到,編譯器可能會在激進的優化設置中優化這種方式。

2

在編譯時:)

(當然,原則上依賴於編譯器等)

1

我不知道他們是在尋找一個答案或幾個可能的答案。

編譯器通常在運行時不做任何事情。編譯器二進制文件及其組件不需要在運行時環境中存在(對於語言C)。

可以消除這個死分支的優化類型將使用某些技術來編寫優化編譯器。參見Muchnik關於編譯器的書。所使用的技術可能涉及創建基本塊的有向圖,然後使用的一種:

  • DEF使用的分析
  • 靜態單分配(SSA)分析
  • 常數傳播沿着(變換如果成如果(3 == 3)
    然後
  • 常量表達式消除
    然後
  • 死代碼/死分支去除

另一方面,某些編譯器可能無法計算出足以在優化期間將其刪除的情況。在這種情況下,如果您使用分支預測在芯片上運行代碼,芯片會「瞭解」第一個分支是預測的,並緩存(獲取)該分支的優勢。但這不是一種編譯器機制,通常不稱爲優化。

1

最簡單的回答: 代碼優化發生在寫作時。

簡單的回答: 也許,它的編譯器依賴於給定的範圍。

難以回答: 考慮到全局範圍,編譯器應該假定它可以在文件的其他地方訪問。多次通過可以優化它。如果編譯器認爲它不是靜態文件(考慮具有平坦內存模型的系統),那麼global就是全球性的,假設應該是任何可以改變值的東西。這就是爲什麼你要避免使用全局變量,除非這個意圖真的是全局訪問。

取決於處理器,分支預測參數將適用,但大部分是編譯時或根本不是。

ps:我真的很不喜歡這樣的面試問題。

+0

同意你的回答,但還有一個疑問出現了,如果全局變量(非易失性)可以在任何時間點改變,那麼爲什麼我們需要易變? – 2012-02-04 19:43:56

+0

對於顯示爲靜態的上下文,需要volatile。考慮指針傳遞的值。此外,如果您直接映射硬件寄存器(考慮外設存儲器映射或設備寄存器),則值可以更改,而無需運行任何代碼。 – Dtyree 2012-02-04 20:02:42

3

不回答這個問題,但舉了一個編譯時優化的例子。當被要求時,gcc優化了代碼。 -O(優化)選項使不同級別的優化成爲可能。它可以用作-O1,-O2和-O3。 gcc手冊頁精確地描述了每個級別的含義。

-S選項將C轉換爲彙編並保存在.s文件中。

test.c的

#include <stdio.h> 

int abc;//Global variable 

void main() 
{ 
    abc = 3; 
    if(abc == 3) 
     printf("abc will be always 3"); 
    else 
     printf("This will never executed"); 
} 

Whitout gcc的優化這兩個字符串出現在彙編代碼。

$ GCC -S test.c的;貓test.s

.file "test.c" 
    .comm abc,4,4 
    .section .rodata 
.LC0: 
    .string "abc will be always 3" 
.LC1: 
    .string "This will never executed" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    movl $3, abc(%rip) 
    movl abc(%rip), %eax 
    cmpl $3, %eax 
    jne .L2 
    movl $.LC0, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
    jmp .L1 
.L2: 
    movl $.LC1, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
.L1: 
    popq %rbp 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 

維特GCC級1優化只有一個字符串被轉換成裝配

$ GCC -O1 -S測試。 c; cat test.s

.file "test.c" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "abc will be always 3" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB11: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, abc(%rip) 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE11: 
    .size main, .-main 
    .comm abc,4,4 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 
+0

優秀....解釋 – 2012-02-10 05:25:04