Concurrency
Memory Ordering
Understand the C++ memory model, instruction reordering, and atomic synchronization modes.
Interview: Compiler/CPU instruction reordering, sequential consistency costs, and acquire-release synchronization semantics.
To optimize performance, compilers and CPUs reorder memory operations. In multi-threaded applications, you specify Memory Ordering on atomic operations to ensure threads see writes in the correct order.
Sequential Consistency
memory_order_seq_cst. The default mode. Guarantees a single global order for all atomic operations across all threads.
Acquire-Release
acquire / release. Synchronizes data write-read paths between specific threads.
Relaxed
memory_order_relaxed. Guarantees atomicity of the operation, but enforces no ordering constraint on surrounding memory operations.
Acquire-Release Semantics
Acquire-release ordering is a common way to synchronize threads without using full sequential consistency:
- Release: When a thread stores a value using
memory_order_release, all its previous writes (even non-atomic ones) become visible to other threads. - Acquire: When a thread reads that value using
memory_order_acquire, it is guaranteed to see all memory writes that occurred before the release store.
Code Walkthrough
Implementing a thread-safe message pass using acquire-release memory ordering.
#include <iostream> #include <atomic> #include <thread> #include <string>std::atomic<bool> g_ready(false); std::string g_data;
void producer() { g_data = "Secret message payload"; // Non-atomic write // Release: ensures data write is visible to acquire reads g_ready.store(true, std::memory_order_release); }
void consumer() { // Acquire: blocks until ready is true and synchronizes previous writes while (!g_ready.load(std::memory_order_acquire)) { // Spin lock } std::cout << "Consumer read: " << g_data << std::endl; }
int main() { std::thread t1(consumer); std::thread t2(producer); t1.join(); t2.join(); return 0; }
Interview-Relevant Information
Q: Why does the compiler reorder instructions?
Answer: The compiler reorders instructions to optimize pipeline execution, combine adjacent memory accesses, and hide memory access latency. However, it assumes a single-threaded execution model, which can lead to bugs in multi-threaded code unless you specify ordering constraints using atomics.
Q: What is the performance cost of memory_order_seq_cst?
Answer: Sequential consistency forces the CPU to use hardware memory barriers to coordinate memory caches across cores. This halts pipeline execution and stalls CPU cores, making it the slowest memory ordering mode.
Quick Checklist
Do you know when to use relaxed vs. sequential consistency ordering? Do you use acquire-release to synchronize data? If yes, your lock-free code is optimized.
Use Cases
Optimizing performance-critical lock-free ring buffers in real-time audio systems.
Writing lightweight atomic statistics counters where update order does not affect program logic.
Common Mistakes
Assuming relaxed ordering guarantees visibility of surrounding memory modifications (it only guarantees atomicity of the target variable).
Using sequential consistency everywhere, introducing unnecessary performance overhead.