Fences are Memory Barriers

Contents[Show]

The key idea of a std::atomic_thread_fence is, to establish synchronisation and ordering constraints between threads without an atomic operation.

std::atomic_thread_fence are simply called fences or memory barriers. So you get immediately the idea, what a std::atomic_thread_fence is all about.

A std::atomic_thread_fence prevents, that specific operations can overcome a memory barrier.

Memory barriers

But what does that mean? Specific operations, which can not overcome a memory barrier. What kind of operations? From a bird's perspective, we have two kinds of operations: Read and write or load and store. So the expression if(resultRead) return result is a load, followed by a store operation.

There are four different ways to combine load and store operations:

  • LoadLoad: A load followed by a load.
  • LoadStore: A load followed by a store.
  • StoreLoad: A store followed by a load.
  • StoreStore: A store followed by a store.

Of course, there are more complex operations, consisting of a load and store part (count++). But these operations didn't contradict my general classification.

But what's about memory barriers?. In case you place memory barriers between two operations like LoadLoad, LoadStore, StoreLoad or StoreStore, you have the guarantee, that specific LoadLoad, LoadStore, StoreLoad or StoreStore operations can not be reordered. The risk of reordering is always given if non-atomics or atomics with relaxed semantic are used. 

Typically, three kinds of memory barriers are used. They are called full fence, acquire fence and release fence. Only in order to remind you. Acquire is a load, release is a store operation. So, what's happening if I place one of the three memory barriers between the four combinations of load and store operations?

  • Full fence: A full fence std::atomic_thread_fence() between two arbitrary operations prevents the reordering of these operations. But that guarantee will not hold for StoreLoad operations. They can be reordered.
  • Acquire fence: An acquire fence std:.atomic_thread_fence(std::memory_order_acquire) prevents, that a read operation before an acquire fence can be reordered with a read or write operation after the acquire fence.
  • Release fence: A release fence std::memory_thread_fence(std::memory_order_release) prevents, that a read or write operation before a release fence can be reordered with a write operation after a release fence.

I admit, that I invested a lot of energy to get the definitions of an acquire and release fence and there consequences for lock-free programming. Especially the subtle difference to the acquire-release semantic of atomic operations are not so easy to get. But, before I come to that point, I will illustrate the definitions with graphics.

Memory barriers illustrated

Which kind of operations can overcome a memory barrier? Have a look at the three following graphics. If the arrow is crossed with a red bar, the fence prevents this kind of operation.

Full fence

FullFence

Of course, you can explicitly write instead of std::atomic_thread_fence() std::atomic_thread_fence(std::memory_order_seq_cst). Per default, sequential consistency is used for fences. Is sequential consistency used for a full fence, the std::atomic_thread_fence follows a global order.

Acquire fence

AcquireFence

Release fence

ReleaseFence

But I can depict the three memory barriers even more concise.

Memory barriers at a glance

loadStore

What's next?

That was the theory. The practice will follow in the next post. In this post, I compare in the first step an acquire fence with an acquire operation, a release fence with a release operation. In the second step, I port a producer consumer scenario with acquire release operations to fences. 

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

Comments   

+1 #1 Doug Barbieri 2016-07-22 16:18
I can't wait to see the next instalment. I'm still completely in the dark as to what to do with a fence!
Quote
0 #2 Tony 2016-08-06 05:31
One of us is confused.

Typically "StoreLoad" etc is describing a type of fence, not a pair of instructions.

There are two typical classifications of fences, Acquire/Release/etc...
and
StoreLoad/LoadStore/etc

The standard chose to go with the first.

Acquire means "after really is after" ie

x = 17;
if (flag.load(memory_order_acquire))
use(data);

The use(data) - read or write - can not move above the Acquire.
But the x = 17 can move down.

Release is the opposite:

use(data);
flag.store(true, memory_order_release);
x = 12;
"before really means before"
use(data) cannot happen after the flag is set, but x = 12 can happen before the flag is set.

Typically Acquire/Release is on read/write instructions, but can be separate barriers.

StoreLoad/LoadStore etc are more Spark-based barriers. They prevent particular movements as their name suggests.
They are always barriers, not tied to read/write instructions.

// x,y,z,w global, r1,r2 local (ie registers)
x = 17;
r1 = y;
StoreLoad
r2 = z;
w = 12;

x = 17 Store can't move below r2 = z Load.
Everything else is free to move.

x = 17;
r1 = y;
StoreStore
r2 = z;
w = 12;
u = 3;

x = 17 Store must happen before w = 12 and u = 3 Stores. u can happen before w.
Everything else can move.

Is that at all what you are trying to say?
Quote
0 #3 Rainer Grimm 2016-08-15 12:47
Hi, I hope I got you right.

I was not talking about pair of operations like you did it with the acquire release operations. I was only talking about fences with an acquire or release semantic. They establish the orderings in my post (LoadLoad, LoadStore, StoreStore).

Fences are global operations and affect the ordering of other
atomic operations in the thread that executed the fence. (Williams) And I described the orderings in this post, used the terms LoadLoad, LoadStore and StoreStore.
Quote
0 #4 Tony 2016-08-17 22:20
Quoting Rainer Grimm:
Hi, I hope I got you right.

...

described the orderings in this post, used the terms LoadLoad, LoadStore and StoreStore.



Ah, it appears that you are using "LoadLoad" etc slightly differently than what I (and most?) are use to.

I think it makes sense now, although I still prefer "before means before" and "after means after" instead of individual read/write combinations.

Thanks!
Quote
0 #5 Rainer Grimm 2016-08-18 05:40
Quoting Tony:



Ah, it appears that you are using "LoadLoad" etc slightly differently than what I (and most?) are use to.

Thanks!


I got my understanding of fences from Jeff Preshing (http://preshing.com/) and Anthony Williams. So my words should be in accordance to theirs.

I like your phrase about (and most). I guess, there are not so many.

Rainer
Quote
0 #6 website 2016-10-27 14:41
I do agree with all the concepts you've presented in your post.
They're really convincing and will certainly work.
Nonetheless, the posts are very quick for newbies. May
you please extend them a little from subsequent time?
Thank you for the post.
Quote
0 #7 Cheap Jordans 2016-11-04 08:39
Spot on with this write-up, I truly believe that this website needs a great deal more attention. I�ll probably be returning to see more,
thanks for the info!
Quote
0 #8 Yeezy 750 2016-11-29 10:42
Useful info. Lucky me I found your website unintentionally, and I'm shocked why
this coincidence didn't took place earlier! I bookmarked it.
Quote

Add comment


My Newest E-Book

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 559

All 255864

Currently are 328 guests and no members online