Memory Model in C++ : sequential consistency and atomicity
The C++ memory model guarantees sequential consistency if you use atomic operations with the appropriate memory orderings to guarantee sequential consistency. If you just use plain non-atomic operations, or relaxed atomics, and no mutexes, then sequential consistency is not guaranteed.
Compilers are free to re-order operations if the difference in behaviour cannot be observed, that's the as-if rule. So for example, if re-ordering sequentially consistent atomics would produce a different observable result then it doesn't meet the as-if rule. If it would not produce a different observable result, then reordering is allowed.
I think I figured out what that slide is talking about, from reading the earlier slides:
slide 12: sequential consistency [Leslie Lamport, 1979]
the result of any execution is the same as-if
- the operations of all threads are executed in some sequential order
- the operations of each thread appear in this sequence in the order specified by their program
slide14: sequential consistency for data-race-free programs
SC-DRF:
- We take care our program does not contain data races
- The system guarantees sequentially consistent execution
So on slide 29, the authors are saying that once you avoid data-race UB using std::atomic
, the program runs as-if everything happened in program order. (If all your std::atomic
operations use the default memory_order_seq_cst
).
This is an interesting way to look at C++'s weak (for non-atomic objects) memory model. This looks like a good set of slides. SC atomic operations are strongly ordered, and are kind of like one-way barriers for non-atomic operations. (And for relaxed atomic operations if you have any).
Note that being data-race-free means you can't look at non-atomic variables at arbitrary times, only when you've established that no other thread is writing them. (Usually via a synchronizes-with relationship with an acquire load seeing a release store done by the writer, or a mutex.) The data-race-free part is the key here; it's very easy to have data-race UB if you're not careful. When compiling to asm for real CPUs, this means non-atomic accesses can work as normal, while atomic<T>
seq_cst accesses need to block compile-time and run-time reordering. https://preshing.com/20120625/memory-ordering-at-compile-time/
Part two: Please don't make a habit of asking two very different questions at once.
This "how does the CPU do it?" question would be a better fit as part of your later question: Atomicity on x86
I have most of an answer to it already written, which I'll put there instead.