我在x64上测试了c ++ 11内存模型的轻松排序语义,我被告知在x86 / 64上只存在存储/加载重新排序,所以我编写了以下程序来测试轻松排序。
理想情况下,如果存在重新排序(它确实存在),那么我的程序应该是获得" g_a == g_b == 0"的情况,但我测试了很长时间,并且永远不会得到预期的结果,任何人都可以帮忙解释原因吗?感谢。
[更新]
抱歉忘记提及我使用的编译器,以下代码在Linux x86 / 64上使用g ++ 4.8.3编译时无法正常工作。感谢@Mat的提醒,然后我尝试使用clang ++ 3.4.2编译它,这次我看到了重新排序,所以它可能是g ++中的一个错误。#include <iostream>
#include <thread>
#include <atomic>
using namespace std;
atomic<int> g_a, g_b;
atomic<int> g_x, g_y;
memory_order order = memory_order_relaxed;
void bar1()
{
register int t = 0;
g_x.store(42, order);
t = g_y.load(order);
g_a = t;
}
void bar2()
{
register int t = 0;
g_y.store(24, order);
t = g_x.load(order);
g_b = t;
}
int main()
{
for (int i = 0; i < 1000000; ++i)
{
g_a = 0; g_b = 0;
g_x = 0; g_y =0;
thread t1(&bar1);
thread t2(&bar2);
t1.join();
t2.join();
if (g_a.load(order) == 0 && g_b.load(order) == 0)
{
cout << "g_a == g_b == 0" << endl;
}
}
}
答案 0 :(得分:3)
为了能够使用正确的内存屏障生成程序集,需要在编译时知道C ++排序参数。
问题出在以下声明中:
memory_order order = memory_order_relaxed;
这不是编译时常量,因此gcc
将假定memory_order_seq_cst
并插入mfence
指令:
Dump of assembler code for function bar1():
0x0000000000400fc0 <+0>: movl $0x2a,0x20128a(%rip) # 0x602254 <g_x>
0x0000000000400fca <+10>: mfence
0x0000000000400fcd <+13>: mov 0x20127d(%rip),%eax # 0x602250 <g_y>
如果您将其更改为:
constexpr memory_order order = memory_order_relaxed;
宽松参数将生效,mfence
消失:
Dump of assembler code for function bar1():
0x0000000000400fc0 <+0>: movl $0x2a,0x201286(%rip) # 0x602250 <g_x>
0x0000000000400fca <+10>: mov 0x20127c(%rip),%eax # 0x60224c <g_y>
如果使用-O3
优化进行编译,则运行二进制文件现在将显示重新排序。