2016-10-28 2 views
0

それはのような数学的な操作だ場合はループの内部状態が常に実行された場合、私は思っていた:私は、次のプログラム書かれているC++で実行されたループでも空ですか?

int margin = 100; 

for(int i=0; i<margin/2;i++);//is margin divided by two at every cycle? 

:別途2つのメソッドをテストするために

long margin = 1000000000, halfOfMargin = margin/2; 
clock_t begin_time; 
clock_t stop_time; 

for(long j=0;j<10;++j) 
{ 
    begin_time = clock(); 
    for(long i=0;i<margin/2;i++); 
    stop_time = clock(); 

    cout <<"With margin/2 :"<< float(stop_time - begin_time)/CLOCKS_PER_SEC<<"\n"; 
} 
for(long j=0;j<10;++j) 
{ 
    begin_time = clock(); 
    for(long i=0;i<halfOfMargin;i++); 
    stop_time = clock(); 
    cout <<"With halfOfMargin :"<< float(stop_time - begin_time)/CLOCKS_PER_SEC<<"\n"; 
} 

を、 (乱雑なコードには申し訳ありません)。

私は、デバッグ設定では分割バージョンが2番目のaproachよりも速いと結論づけました。私が驚いたのは、Releaseに切り替えると、毎回各バージョンごとに0の出力が得られました。for命令が実際に実行された場合には不可能でした(デバッグ構成では、各出力の時間は約2秒でした半分)。

私の質問は、それらが不必要であると判断した場合にコンパイラの指示をスキップすることが可能な場合、なぜそうでない場合はその2つのケースで時間が異なるかということです。

注:私はVisual Studio 2012 expressをIDEとしてデフォルトのコンパイラで使用しています。

+1

オプティマイザは通常、スキップするのに非常に優れています。 – SLaks

+0

f10とブレークポイントを使用して何が実行されるのか、実行されないのかを調べるだけです。 –

+1

些細なことに時間を掛けようとするときに回避しなければならないことは一般的なことです。あなたが本当にアセンブリを確認することを知りたい場合。 – NathanOliver

答えて

4

C++標準では抽象マシンの動作を記述しています。

一部の動作は外部から観察可能です。

エフェクトが観測できない動作は、「そのまま」ルールとしてスキップされます。

これは、コンパイラが無用なコードを自由にスキップできることを意味します。状況によっては、無限のループをスキップすることもできます。これは誰も目撃することができないことを証明し、ループの終了を許可する状態に設定し、ループを終了します。

"debug"では、コンパイラの最適化がはるかに遅くなります。

2

はいこれはコンパイラが行います。これはas-if ruleに該当します。観察可能な振る舞いが変わらない限り、コードを再編成することができます。ループ内のiへの変更は、何か(コンパイラが本体やIO関数を見ることができない関数)によっては観測できないため、スピードアップすることができます。

あなたが打ち鳴らすか、GCCによって生成されたコードを見ると、それはまた、アウトに最適化されます:

void foo(int n) 
{ 
    int i; 
    for(i = 0; i < n/2; ++i); 
    sink(i); 
} 

clang

foo(int):        # @foo(int) 
# BB#0: 
     #DEBUG_VALUE: foo:n <- %EDI 
     #DEBUG_VALUE: foo:i <- 0 
     mov  eax, edi 
     shr  eax, 31 
     add  eax, edi 
     xor  ecx, ecx 
     sar  eax 
     cmovs eax, ecx 
     mov  edi, eax 
     jmp  sink(int)    # TAILCALL 

gcc

foo(int): 
     mov  eax, edi 
     shr  eax, 31 
     add  eax, edi 
     xor  edi, edi 
     sar  eax 
     test eax, eax 
     jle  .L2 
.L3: 
     add  edi, 1 
     cmp  edi, eax 
     jne  .L3 
.L2: 
     jmp  sink(int) 
3

として、 'margin'変数はローカルで変更されていないので、コンパイラは決してchanを見ることができませんテストを一度だけ行います。

関数を呼び出して結果をテストする場合、関数を呼び出すことはできません。

変数定義にvolatileを追加してもう一度実行してください。コンパイラは、変数が別のスレッドから変更され、ループごとにテストを実行できると考えます。

関連する問題