Skip to content

Instantly share code, notes, and snippets.

@widlarizer
Last active March 7, 2024 11:01
Show Gist options
  • Save widlarizer/f771dce024b19d48d4f58f5ea2f33ceb to your computer and use it in GitHub Desktop.
Save widlarizer/f771dce024b19d48d4f58f5ea2f33ceb to your computer and use it in GitHub Desktop.
Atomic language features comparison

Atomic language features comparison - a compiler developer's point of view

Languages explained

  • made up by Intel for the Itanium ABI
  • legacy
  • all sequentially consistent
  • operate on arbitrary integer-based types
  • made up by GCC
  • take memorder args
  • operate on arbitrary integer-based types
  • operate on _Atomic type types
  • by default sequentially consistent, but have _explicit versions which take memorder arguments
  • better than builtins
  • operate on std::atomic<T> types
  • these aren't functions but methods
  • operate on std::sync::atomic::AtomicSomething types
  • take memorder arguments

The megatable

Atomic operation __sync builtin __atomic builtin C11 C++14 Rust
fence __sync_synchronize __atomic_thread/signal_fence1 atomic_thread/signal_fence1 std::atomic_thread/signal_fence1 fence, compiler_fence (only for reordering by the compiler)
atomic compare and swap __sync_bool/val_compare_and_swap (the “bool” version returns true if the comparison is true and newval is written. The “val” version returns the contents of *ptr before the operation)  __atomic_compare_exchange atomic_compare_exchange_weak/strong2 compare_exchange_weak/strong2 compare_and_swap/exchange (returns new value), compare_exchange (returns Result<new_value>), compare_exchange_weak2
atomic swap None. Only in clang: __sync_swap __atomic_exchange atomic_exchange exchange swap
atomic add __sync_fetch_and_add, __sync_add_and_fetch34 __atomic_fetch_add, __atomic_add_fetch34 atomic_fetch_add. Also: sub, or, xor, and operator+= Also: -=, &=, |=, ^= (but also fetch_add equivalent to the operators) fetch_add (Also: sub, or, xor, and, nand, max, min), or fetch_update with modification function
load None __atomic_load atomic_load operatorT5 load
store None __atomic_store atomic_store operator= store
flag operations (redundant, fairly useless...) __sync_lock_test_and_set (atomic swap, may be restricted to only some values on some backends for historical reasons), __sync_lock_release (atomic store 0) - Have acquire and release barriers respectively, not seq_cst! __atomic_test_and_set (atomic swap some_magic_value, bool return value), __atomic_clear (atomic store 0). Only for bool and char atomic_flag_test_and_set (atomic swap true), atomic_flag_clear (atomic store 0). Only for atomic_flag type std::atomic_flag methods: test_and_set, clear, test None. There's probably enough cool types in std::sync that you don't need these
wait, notify None None None std::atomic_flag methods: wait, notify_one, notify_all atomic_wait::{wait, wake_one, wake_all};
lock-free check None __atomic_always_lock_free/__atomic_is_lock_free atomic_is_lock_free is_lock_free None
type initialization No special atomic type No special atomic type atomic_init constructor new

Just about every builtin has a version with _N (where N is type size in bytes) suffix, which returns old value, instead of void, and also takes the value argument by value, not by reference. These are expected to be implemented by external libraries (in our case compiler-rt) to be deferred the operation to by the compiler by lowering an operation into a libcall. These _N suffix functions are not really meant to be manually called, except it might be better, since they have proper return values, instead of writing return values (like the result of a load) to a pointer argument. To illustrate: type __atomic_load_n (type *ptr, int memorder) VS void __atomic_load (type *ptr, type *ret, int memorder)

Footnotes

  1. thread: between threads. signal: between thread and signal handlers (not very applicable) 2 3

  2. The weak forms of the functions are allowed to fail spuriously. When a compare-and-exchange is in a loop, the weak version will yield better performance on some platforms 2 3

  3. One returns the old value, the other returns the new value 2

  4. Also: sub, or, and, xor, nand. Only in clang: max, umax, min, umin. __sync min, max, umin, umax: only exist for int (not overloaded for char etc) 2

  5. This means you can just cast it to the underlying type, and that will load the current value: std::atomic<int> a = 1; int b = (int)a;. But there's still store and load methods (equivalent to the operators)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment