5

I was testing the relaxed ordering semantic of c++11 memory model on x64, and I was told that on x86/64 only store/load reordering exists, so I wrote the following program to test the relaxed ordering.

Ideally, if reordering exists(which it does), then my program should hit the case of getting "g_a == g_b == 0", but I tested it for a long time, and never get the expected results, could anybody help explain why? thanks.

[update]

sorry for forgetting to mention the compiler I used, the following code won't work when compiled with g++ 4.8.3 on Linux x86/64. thanks to @Mat's reminder, I then try compile it using clang++ 3.4.2, this time I saw the reordering, so it might be a bug in 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; } } } 
3
  • 2
    I haven't looked at your code, but you should be aware that x86 provides stronger guarantees than you might need. Commented Nov 23, 2014 at 8:06
  • I am aware of that, and that is why I choose the reordering type of "store/load" for testing which I know for sure exist. Commented Nov 23, 2014 at 8:17
  • 1
    FWIW, I'm seeing your "expected" results when building with clang++ 3.5 (-O3 -std=c++11 -pthread using libstdc++ 4.7.3) on Linux/x86_64. Not seeing this with GCC 4.9.2. Not seeing it with clang++ using libc++. Commented Nov 23, 2014 at 8:31

1 Answer 1

3

To be able to generate assembly with the correct memory barriers, C++ ordering parameters need to be known at compile time.

The problem lies in the following statement:

memory_order order = memory_order_relaxed; 

This is not a compile time constant, therefore gcc is going to assume memory_order_seq_cst and insert an mfence instruction:

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> 

If you change it to:

constexpr memory_order order = memory_order_relaxed; 

The relaxed parameter will take effect and the mfence is gone:

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> 

If you compile with -O3 optimization, running the binary will now show the re-ordering.

Sign up to request clarification or add additional context in comments.

2 Comments

wow, this make sense. thanks. maybe the compiler should emit some warning though.
Yeah, that would be ideal. Not sure if it can though since you are not violating a language rule

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.