2017-03-14 14 views
3

は、私は、コードのこの部分を持っている:-O3ループ増分の最適化

#include <iostream> 
#include <thread> 

long int global_variable; 

struct process{ 
    long int loop_times_ = 0; 
    bool op_; 
    process(long int loop_times, bool op): loop_times_(loop_times), op_(op){} 

    void run(){ 
     for(long int i=0; i<loop_times_; i++) 
      if (op_) global_variable+=1; 
      else global_variable-=1; 
    } 

}; 

int main(){ 
    struct process p1(10000000, true); 
    struct process p2(10000000, false); 

    std::thread t1(&process::run, p1); 
    std::thread t2(&process::run, p2); 
    t1.join(); 
    t2.join(); 

    std::cout <<global_variable<< std::endl; 
    return 0; 
} 

主な機能は、インクリメントおよびグローバル変数をデクリメント二つのスレッドを起動します。 これでコンパイルすると:

g++ -std=c++11 -o main main.cpp -lpthread 

私は実行ごとに異なる出力を得ます。 しかし、私はこれで-O3を追加してコンパイルした場合:

g++ -O3 -std=c++11 -o main main.cpp -lpthread 

出力は私のクリティカルセクションを排除し、ここで何が起こっているの最適化のどのような種類のたびに

ゼロであり、どのようにiは、コンパイラをだますことができますそれを最適化しない?

EDIT:OS:Ubuntuの16.04.4、G ++:5.4.0

+0

これは未定義の動作です。 – NathanOliver

+1

現在のコードの結果は、コンパイル時に計算できます。プログラムの2つの異なる引数として反復回数を渡して、このように実行しようとしましたか? ./a.out 10000000 10000000 – mrks

+1

より速い最適化では、最初のスレッドはもう一方のスレッドが開始する前に終了することもあると思います。時間がかかり、振る舞いが元に戻るまで数を増やしてください。 – Baldrick

答えて

2

それはあなたのrunメソッドが相当に最適化されていることは非常に可能性があります:これは、コンパイラが入手可能な情報に非常に簡単に行うことができるものである

void run(){ 
     if (op_) global_variable += loop_times_; 
      else global_variable -= loop_times_; 

コンパイラを騙すには、ループが追加または削除されることが明らかでないことを確認する必要があります。他の副作用はありません。すべての繰り返しで

ループ内に関数呼び出しを追加すると、totalIterationsDoneなどのオブジェクト上の単純なカウンタがインクリメントされます。これによりコンパイラは実際にループを実行することがあります。ループ変数を引数として渡すと、中間値の追跡を強制的に行うこともできます(i)。

struct process{ 
    long int loop_times_ = 0; 
    bool op_; 
    long int _iterationsDone = 0; 
    process(long int loop_times, bool op): loop_times_(loop_times), op_(op){} 

    void run(){ 
     for(long int i=0; i<loop_times_; i++){ 
      if (op_) global_variable+=1; 
      else global_variable-=1; 
      Trick(i); 
     } 
    } 

    void Trick(int i){ 
     _iterationsDone += 1; 
    }  
}; 
+0

ループのiを引数としてトリックを変更した場合のみ、異なる出力があります。ありがとう! –

+0

問題ありません。それは実際には良い意味です。私は解答に合わせて私の解答を編集します。 – Baldrick

0

あなたのプログラムは、データレースの形で、未定義の動作をしています。同期なしで1つの変数にアクセスする2つのスレッドはデータ競合であり、したがって未定義です。

データ競合を削除する最も簡単な方法は、global_variableをアトミックにするために、次のようになります。

std::atomice<long int> global_variable; 

これ以上の変更は、コードの残りの部分で必要ではないはずです。

+0

しかし、私はデータ競争を排除したくありません。私は実行が-O3のときにエフェクトが見えない理由を理解していません –

+4

@k_kaz **未定義の動作は未定義です** UBを出展しているプログラムについては何も言えません - 代わりにピザをオンラインで注文することもできますグローバル変数で作業する – Angew

+0

@k_kaz *なぜ*あなたはデータ競争が欲しいですか?なぜあなたは未定義の振る舞いをしたいのですか?それらは悪いものとみなされていますか? –