-O3循环增量优化

时间:2017-03-14 15:02:34

标签: c++ compiler-optimization critical-section

我有这段代码:

#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

每次输出为零

这里发生了什么样的优化消除了我的关键部分,我如何欺骗编译器不优化它?

编辑:操作系统:Ubuntu 16.04.4,g ++:5.4.0

2 个答案:

答案 0 :(得分:2)

您的run方法很可能正在优化到相当于:

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

编译器可以使用现有信息轻松完成这项工作。

要欺骗编译器,你必须确保循环在每次迭代时都不会添加或减去1 而没有其他副作用

尝试在循环中添加一个函数调用,只是增加一个名为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;
    }    
};

答案 1 :(得分:0)

您的程序以数据竞争的形式具有未定义的行为。在没有同步的情况下访问一个变量的两个线程是数据竞争,因此未定义。

删除数据竞争的最简单方法是使global_variable原子:

std::atomice<long int> global_variable;

其余代码不需要进一步更改。