1

我试图编写一些代码来观察内存操作的重新排序。

在下面的示例中,我预计在 set_values() 的某些执行中,分配值的顺序可能会发生变化。特别是 notification = 1 可能发生在其余操作之前,但即使在数千次迭代之后也不会发生。我用 -O3 优化编译了代码。这是我指的 youtube 材料:https ://youtu.be/qlkMbxUbKfw?t=200

int a{0};
int b{0};
int c{0};
int notification{0};

void set_values()
{
    a = 1;
    b = 2;
    c = 3;
    notification = 1;
}

void calculate()
{
   while(notification != 1);
   a += b + c;
}

void reset()
{
    a = 0;
    b = 0;
    c = 0;
    notification = 0;
}

int main()
{
    a=6; //just to allow first iteration

    for(int i = 0 ; a == 6 ; i++)
    {
        reset();
        std::thread t1(calculate);
        std::thread t2(set_values);

        t1.join();
        t2.join();
        std::cout << "Iteration: " << i << ", " "a = " << a << std::endl;
    }
    return 0;
}

现在程序陷入无限循环。我希望在某些迭代中 set_values() 函数中的指令顺序可以改变(由于现金内存的优化)。例如 notification = 1 将在 c = 3 之前执行什么将触发执行 calculate() 函数并给出 a==3 什么满足终止循环的条件并证明重新排序

或者也许有人可以提供其他简单的代码示例来帮助观察内存操作的重新排序?

4

2 回答 2

0

编译器确实可以重新排序函数中的分配set_values。但是,不需要这样做。在这种情况下,没有理由重新排序任何内容,因为您正在为所有四个变量分配常量。

现在程序陷入无限循环。

这可能是因为while(notification != 1);将优化为无限循环。

通过一些工作,我们可以找到一种方法让编译器notify = 1在其他语句之前重新排序分配,请参阅https://godbolt.org/z/GY-pAw。请注意,程序x从标准输入读取,这样做是为了强制编译器从内存位置读取。

我还使变量notificationvolatile,因此while(notification != 1);不会被优化掉。

你可以在你的机器上尝试这个例子,我已经能够使用在 Intel Sandy Bridge cpu 上运行的 g++9.2 和 -O3 始终使断言失败。

请注意,如果指令彼此独立,CPU 本身可以重新排序指令,请参阅https://en.wikipedia.org/wiki/Out-of-order_execution。但是,要始终如一地测试和重现这有点棘手。

于 2019-11-05T23:26:33.960 回答
0

您的编译器以意想不到的方式进行优化,但因为您违反了 C++ 内存模型的基本规则,所以允许这样做。

如果其中至少一个是写入器,则不能从多个线程访问内存位置。

要同步,请使用 astd:mutex或使用std:atomic<int>而不是int您的变量

于 2019-11-06T13:07:23.697 回答