2013-07-22 19 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 ...を使用)。処理時間を測定した。私は実行時間が2.444sに対して0.001sであったことを知り、非常に驚​​きました。そしてこれはあまり意味がありません。または、むしろ、この背後にいくつかの論理がなければならない。

何が起こっているのか、この問題を緩和する方法を教えてください。

私は膨大な量のデータを処理するプログラムに取り組んでいます。私は同じパフォーマンス問題に遭遇しているようです。

浮動小数点数から整数に切り替えることを検討していましたが、整数では同じように動作するようです。

EDIT:最後に解決策は簡単で論理的でした。だから私はすべての答えと説明に感謝します!

+0

生成されたコードを見ましたか? –

+0

あなたは正確に何とか反復していますか?このような依存関係が改善されているもののための余地はあまりありません(どのbtwが非常に速く収束しているようですが、反復回数が多いと仮定するとテスト可能になると思います)。 – Mysticial

+0

'a' 「揮発性」である。 – jxh

答えて

13

最初の例では、計算値は一定でした。コンパイラはc = 0.7 * 0.577 - 0.2 * 0.921を計算し、コンパイル時間はになります。ループ内で何も変わらないので、ループを最適化することもできます(ab & cは不変です)。

第2の例では、acは反復ごとに異なりますので、100000000回計算する必要があります。

+0

はい。最初の行では、cはコンパイラによって定数に設定されました。 – Jiminion

+2

@Jim:いいえ、2行目は定数ではありません。ループの各反復において、cはaに依存し、aはcに依存する。何も一定ではありません。 –

+0

@Billy、そうです。 – 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

最適なオプティマイザは非常に優れています。

1回の計算ですべての反復で同じ値が返されるため、ループ内の何かを再計算する必要はないため、オプティマイザでは計算されません。

aも(2行の計算で)変更すると、ループを実行する必要があります。

したがって、タイミングの違い。

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;という行がコメントアウトされている場合、ループ内の唯一の式はループ不変です。オプティマイザはそれを知っているので、計算をループ外に移動します。その後、オプティマイザはループ内に何もないことを認識し、ループを取り除きます。

行を戻すと、式はもはやループ不変ではなくなります。