Search code examples
c++c++11atomicmemory-barriersstdatomic

What is the difference between load/store relaxed atomic and normal variable?


As I see from a test-case: https://godbolt.org/z/K477q1

The generated assembly load/store atomic relaxed is the same as the normal variable: ldr and str

So, is there any difference between relaxed atomic and normal variable?


Solution

  • The difference is that a normal load/store is not guaranteed to be tear-free, whereas a relaxed atomic read/write is. Also, the atomic guarantees that the compiler doesn't rearrange or optimise-out memory accesses in a similar fashion to what volatile guarantees.

    (Pre-C++11, volatile was an essential part of rolling your own atomics. But now it's obsolete for that purpose. It does still work in practice but is never recommended: When to use volatile with multi threading? - essentially never.)

    On most platforms it just happens that the architecture provides a tear-free load/store by default (for aligned int and long) so it works out the same in asm if loads and stores don't get optimized away. See Why is integer assignment on a naturally aligned variable atomic on x86? for example. In C++ it's up to you to express how the memory should be accessed in your source code instead of relying on architecture-specific features to make the code work as intended.

    If you were hand-writing in asm, your source code would already nail down when values were kept in registers vs. loaded / stored to (shared) memory. In C++, telling the compiler when it can/can't keep values private is part of why std::atomic<T> exists.

    If you read one article on this topic, take a look at the Preshing one here: https://preshing.com/20130618/atomic-vs-non-atomic-operations/

    Also try this presentation from CppCon 2017: https://www.youtube.com/watch?v=ZQFzMfHIxng


    Links for further reading:


    Also see Peter Cordes' linked article: https://electronics.stackexchange.com/q/387181
    And a related one about the Linux kernel: https://lwn.net/Articles/793253/

    No tearing is only part of what you get with std::atomic<T> - you also avoid data race undefined behaviour.