2 ms·
Atomics are not magical, usually there's a type of "lock" at the cache line level (actually a lot of message passing across cores). Using the memory ordering r
by juancn 2y ago
Atomics are not magical, usually there's a type of "lock" at the cache line level (actually a lot of message passing across cores).
Using the memory ordering relaxed basically only guarantees there won't be word tearing.
For example consider the following:
// T1:
r1 = load_relaxed(y); // A
store_relaxed(x, r1); // B
// T2:
r2 = load_relaxed(x); // C
store_relaxed(y, 42); // D
Which implies that it's legal to read r1 == r2 == 42, because even if:
- in T1 A happens before B (A ~> B)
- in T2 C happens before D (C ~> D)
nothing prevents D from happening before A in the modification order of y and C happening before B on the modification order of x.
In any case, on Intel processors, most accesses not crossing cache line boundaries are somewhat atomic beyond relaxed, which brings about another phenomena: false sharing.
Which essentially boils down to a performance penalty due to two threads accessing data on the same cache line, even if it's not the same word.
Another thing that can affect measurements like this is task/core affinity. The cost to coordinate cache line synchronization is different if both are on the same core but different hyper-threads, two cores on the same socket (with some variation on which cores) and if they are on different sockets (big/little architectures bring their own complications too).
In the article's example, it would be valid for the counter to never be flushed (not on x86), as long as there's enough cache capacity, effectively making the polling thread read always the same number.
Although this is very unlikely on realistic workloads.
I would have used release/acquire semantics to get predictable capture of data and a not too aggressive polling behavior so as not to compete for CPU caches with useful workloads.