What's the difference between specialized and non-specialized atomic member functions?
As can be seen in the synposis of these classes on the standard (§29.5), there are three different sets of member functions:
- the most generic one provides only store, load, exchange, and compare-exchange operations;
- the specializations for integral types provide atomic arithmetic and bitwise operations, in addition to the generic ones;
- the specialization for pointers provides pointer arithmetic operations in addition to the generic ones.
What's the difference (if there is any) between following functions?
operator= stores a value into an atomic object (public member function) v.s. store (C++11) atomically replaces the value of the atomic object with a non-atomic argument (public member function)
(...)
The main functional difference is that the non-operator versions (§29.6.5, paragraphs 9-17 and more) have an extra parameter for specifying the desired memory ordering (§29.3/1). The operator versions use the sequential consistency memory ordering:
void A::store(C desired, memory_order order = memory_order_seq_cst) volatile noexcept;
void A::store(C desired, memory_order order = memory_order_seq_cst) noexcept;
Requires: The order argument shall not be memory_order_consume, memory_order_acquire, nor memory_order_acq_rel.
Effects: Atomically replaces the value pointed to by object or by this with the value of desired. Memory is affected according to the
value of order.
C A::operator=(C desired) volatile noexcept;
C A::operator=(C desired) noexcept;
Effects: store(desired)
Returns: desired
The non-operator forms are advantageous because sequential consistency is not always necessary, and it is potentially more expensive than the other memory orderings. With careful analysis one can find out what are the minimal guarantees needed for correct operation and select one of the less restrictive memory orderings, giving more leeway to the optimizer.
What's the downside of declare a variable as atomic v.s. a non-atomic variable. For example, what's the downside of std::atomic<int> x v.s. int x? In other words, how much is the overhead of an atomic variable?
Using an atomic variable when a regular variable would suffice limits the number of possible optimizations, because atomic variables impose additional constraints of indivisibility and (possibly) memory ordering.
Using a regular variable when an atomic variable is needed may introduce data races, and that makes the behaviour undefined (§1.10/21):
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
The overhead of an atomic variable is a matter of quality of implementation. Ideally, an atomic variable has zero overhead when you need atomic operations. When you don't need atomic operations, whatever overhead it may have is irrelevant: you just use a regular variable.
Which one has more overhead? An atomic variable, v.s. a normal variable protected by a mutex?
There's no reason for an atomic variable to have more overhead than a normal variable protected by a mutex: worst case scenario, the atomic variable is implemented just like that. But there is a possibility that the atomic variable is lock-free, which would involve less overhead. This property can be ascertained with the functions described in the standard in §29.6.5/7:
bool atomic_is_lock_free(const volatile A *object) noexcept;
bool atomic_is_lock_free(const A *object) noexcept;
bool A::is_lock_free() const volatile noexcept;
bool A::is_lock_free() const noexcept;
Returns: True if the object’s operations are lock-free, false otherwise.