2017-10-27 7 views
6

私は次のように単純化できる状況に遭遇したC++コードを最適化しました。gccの最適化は明らかに一定の変数を持つループに影響します

#include <iostream> 
#include <thread> 

using namespace std; 

bool hit = false; 

void F() 
{ 
    this_thread::sleep_for(chrono::seconds(1)); 
    hit = true; 
} 

int main() 
{ 
    thread t(F); 

    while (!hit) 
     ; 

    cout << "finished" << endl; 
    t.join(); 
    return 0; 
} 

これは基本的に第二後truehitの値を変化させるスレッドを開始:

、このコードを検討します。同時に、コードはhitの値がtrueになるまで続く空のループに入ります。私はgcc-5.4-gフラグを使用してこれを編集し、すべてうまくいきました。コードはfinishedを出力して終了します。しかし、私は-O2フラグを付けてコンパイルしましたが、今度はコードが無限ループに詰まってしまいました。

解体を見てみると、コンパイラが無限ループの根本的な原因であるとして、次の、生成された:JMP 0x6ba6f3

を! 0x00000000006ba6f3

OK、そうはっきりと、コンパイラはhitの値がfalseであると推定していると、それは別のスレッドがその値を変更することが考慮せずに無限ループであることを前提としない、なぜそれがとてもループ内で変更されることはありません!この最適化モードは、上位レベル(-O2)に追加されています。私は最適化フラグのエキスパートではないので、誰がこの結果を担当しているか教えてもらえますか?それをオフにすると、他のコードに大きなパフォーマンス上のコストがかかりますか?つまり、このコードパターンはどれくらい稀ですか?

+1

は 'std :: atomic 'を使用します。 – Jarod42

+2

ヒットをvolatileと宣言するとどうなりますか? – Milack27

+0

@ Milack27はい、それは問題を解決します!私はまだ分かっていないC++には多くのものがあります! – Sinapse

答えて

6

このコードには未定義の動作があります。 hitを1つのスレッドから変更していて、別のスレッドと同期して読み込みません。

hitからfalseを最適化することは、未定義の動作の有効な結果です。これはhitstd::atomic<bool>とすることで解決できます。これはよく定義されている場合に行い、最適化をブロックします。

+0

はいこれも問題を解決します(私は揮発性が私の場合より効率的になると思う)が、OMG !!変数をアトミックとして定義すると、コンパイラはそれをどのように知っていますか?あなたが修飾子を追加しているので、私は揮発性の意味を意味するが、std :: atomicの場合、コンパイラはそれが別のスレッドで変更される可能性があることを推測する方法は? – Sinapse

+0

どうしよう! 'std :: atomic 'クラスには揮発性のメンバがあります。 :) g ++のインクルードフォルダから "アトミック"ファイルを開こうとすると、そこに表示されます。 – Milack27

+0

ニース、それは価値があった! – Sinapse

2

複数のスレッドから同時にhitを読み書きするには、ある種の同期が必要です。そうでない場合は、競合状態が発生します。 hitstd::atomic<bool>にするか、hit値にアクセスするときにロックする必要があるmutexを追加することができます。スレッドを終了するのを待つだけの場合は、フラグを追加することなく、ちょうどthread.join()のままにして(その後に「終了」してください)残すことができます。

1

hitを揮発性と宣言すると、この変数は外部の要因によっていつでも変更できるので、コンパイラはその値がmain関数に沿って変更されないと想定しません。

hit変数に書き込むスレッドが1つのみである限り、コードは競合状態がなく、正常に動作するはずです。しかし、複数のスレッドを扱うときは、ここでの他の答えですでに述べたように、アトミックオブジェクト、mutex、セマフォなどの同期ツールを使用することは常に安全です。

+0

'volatile' [必要な注文を提供できません](https://stackoverflow.com/a/4558031/15416)。 – MSalters

+0

はい。私が言ったように、_itは常に同期化ツールを使用する方が安全です。 'volatile 'を使うと、このケースでは正しく動作するはずです。 – Milack27

関連する問題