2013-07-22 53 views
6

有人可以用這個小程序解釋這裏發生了什麼嗎?算術運算的比例處理時間[C]

#include<stdio.h> 

int main() 
{ 
    float a=0.577; 
    float b=0.921; 
    float c; 
    int i; 

    for (i=0;i<100000000;i+=1){ 
     c=0.7*a-0.2*b; 
     //a=0.145*c+2.7*b; 
    } 

    printf ("%.3f\n",c); 
} 

請注意,有一條線被註釋掉。

我編譯它首先沒有行,然後與行。 (使用gcc -O2 ...)。並測量了處理時間。我很驚訝地發現執行時間是0.001s2.444s。這沒有多大意義。或者說,這背後必然有一些邏輯。

你能解釋一下怎麼回事,以及如何緩解這個問題?

我對處理大數據量的程序工作,在我看來,我遇到了同樣的性能問題存在。

我正在考慮從浮標到整數轉換,但似乎與整數它的行爲是相同的。

編輯:最後,解決方案是微不足道的和合乎邏輯的。所以我感謝所有的答案和解釋!

+0

你看看生成的代碼嗎? –

+0

你到底在做什麼?我沒有看到有這樣一個依賴鏈的東西有很大的空間需要改進...(哪個btw,似乎很快就會收斂,我認爲高迭代次數只是爲了讓它可測試) – Mysticial

+0

嘗試聲明'a'與'volatile'。 – jxh

回答

13

在第一種情況下,計算值是恆定的。編譯器將計算c = 0.7 * 0.577 - 0.2 * 0.921編譯時間。它甚至可以自由地優化循環,因爲它內部沒有任何變化(ab & c是不變的)。

在第二種情況下,ac因每次迭代而不同,因此必須計算100000000次。

+0

是的。在第一行中,c被編譯器設置爲常量。 – Jiminion

+2

@Jim:不,第二行不是恆定的。在循環的每次迭代中,c依賴於a,然後a依賴於c。沒什麼是不變的。 –

+0

@比利,你是對的。 – Jiminion

2

沒有註釋掉的行,編譯器可以優化整個循環。所設置的值不會因循環而改變。

隨着註釋掉線,在循環的每個開始a的值發生變化,因此該循環不能被優化了。

也就是說,你的程序,這一個:

int main() 
{ 
    float a=0.577; 
    float b=0.921; 
    float c; 
    int i; 

    c=0.7*a-0.2*b; 
    for (i=0;i<100000000;i+=1){ 
     //a=0.145*c+2.7*b; 
    } 

    printf ("%.3f\n",c); 
} 

產生相同的答案,當且僅當該行被註釋掉。

3

好的優化器非常好。

由於一個行計算返回在每個迭代上相同的值,就沒有必要重新計算循環什麼,所以優化不會。

當你改變a太(與兩線計算),那麼它必須運行循環。

因此在時間上的差異。

2

這裏是我的代碼進行編譯與優化您的範例中得到啓用:

(__TEXT,__text) section 
_main: 
0000000100000f20 pushq %rbp 
0000000100000f21 movq %rsp, %rbp 
0000000100000f24 leaq 61(%rip), %rdi ## literal pool for: %.3f 

0000000100000f2b movsd 45(%rip), %xmm0 
0000000100000f33 movb $1, %al 
0000000100000f35 callq 0x100000f3e ## symbol stub for: _printf 
0000000100000f3a xorl %eax, %eax 
0000000100000f3c popq %rbp 
0000000100000f3d ret 

注意,循環甚至不運行 - 編譯器優化了它完全的,因爲它可以告訴大家,只分配c重要的是最後一個。

與此相反,在註釋行重新插入,循環必須運行,輸出的代碼如下所示:

(__TEXT,__text) section 
_main: 
0000000100000ea0 pushq %rbp 
0000000100000ea1 movq %rsp, %rbp 
0000000100000ea4 movss 148(%rip), %xmm5 
0000000100000eac movl $100000000, %eax 
0000000100000eb1 movsd 143(%rip), %xmm1 
0000000100000eb9 movsd 143(%rip), %xmm2 
0000000100000ec1 movsd 143(%rip), %xmm3 
0000000100000ec9 movsd 143(%rip), %xmm4 
0000000100000ed1 nopw %cs:(%rax,%rax) 
0000000100000ee0 xorps %xmm0, %xmm0 
0000000100000ee3 cvtss2sd %xmm5, %xmm0 
0000000100000ee7 mulsd %xmm1, %xmm0 
0000000100000eeb addsd %xmm2, %xmm0 
0000000100000eef cvtsd2ss %xmm0, %xmm0 
0000000100000ef3 cvtss2sd %xmm0, %xmm0 
0000000100000ef7 movaps %xmm0, %xmm5 
0000000100000efa mulsd %xmm3, %xmm5 
0000000100000efe addsd %xmm4, %xmm5 
0000000100000f02 decl %eax 
0000000100000f04 cvtsd2ss %xmm5, %xmm5 
0000000100000f08 jne 0x100000ee0 
0000000100000f0a leaq 87(%rip), %rdi ## literal pool for: %.3f 

0000000100000f11 movb $1, %al 
0000000100000f13 callq 0x100000f1C## symbol stub for: _printf 
0000000100000f18 xorl %eax, %eax 
0000000100000f1a popq %rbp 
0000000100000f1b ret 

完全不同的,因爲你可以看到。

+0

它看起來像機器+彙編代碼。我怎樣才能得到這個代碼在gcc上? – haccks

+2

我剛剛在我的Mac上使用'otool'反彙編。如果你在linux上,你可以使用'objdump'。或者,編譯器本身會用'-S'標誌生成程序集(儘管它通常不易讀)。 –

+1

彙編程序已經將彙編代碼轉換爲機器代碼... –

2

隨着行a=0.145*c+2.7*b;註釋掉,循環中唯一的表達式是循環不變的。你的優化器知道,所以它將計算移出循環。然後優化器注意到循環中沒有任何東西,所以它擺脫了循環。

當你放回線時,表達式不再是循環不變的。