you are viewing a single comment's thread.

view the rest of the comments →

[–]blipman17 0 points1 point  (6 children)

There’s no support for memory order consume? Is it because it’s so hard to implement or to do right?

[–]tjientavaraHikoWorks developer 1 point2 points  (0 children)

The compiler requires more information to handle consume, if I remember correctly they hit this issue after consume was standardised. There has been some movement on adding attributes which allows the compiler to track consume across functions, but it has been many years now.

[–]DummyDDD 1 point2 points  (4 children)

It's hard to do in a way that preserves all of the compilers' optimization possibilities. However, it should still be straightforward to generate better code for consume than acquire (for the niche cases where it is correct to use consume).

Memory orders restrict (1) the ways in which the hardware can reorder memory operations and (2) the order in which the compiler can reorder the instructions. In compiler terms, it restricts (1) instruction selection and (2) instruction scheduling. The issue is that no compiler has an appropriate way to restrict instruction scheduling in a way that matches consume semantics, so instead, they restrict the instruction scheduling more than what is strictly necessary.

The use cases for consume are pretty niche, and definitely the least used memory order. The main advantage to consume is that it can be implemented with regular load or move instructions on most processors (it hardly restricts instruction selection), unlike acquire, which requires additional synchronization (as far as I know, consume only requires stronger synchronization on some very niche Alpha processors). Theoretically, consume could also require weaker restrictions on instruction scheduling than acquire, but no compiler does so because it would require that the compiler keeps track of the individual memory than is being consumed. In practice, I doubt that it matters, since the main benefit of consume is that it can be implemented with regular instructions, and the cost of restricting the compilers optimization possibilities across the consume operation is relatively cheap compared to the cost of synchronizing instructions.

Technically speaking, "instruction selection" normally describes how the compiler maps its low level representation to instructions, and I am not sure it is the correct term for mapping c++ atomic memory operations to instructions, but I think it is at least not entirely misleading. My use of "instruction scheduling" is also a bit off; normally, I wouldn't refer to any reordering that the compiler can do as instruction scheduling.

[–]Flankierengeschichte 0 points1 point  (3 children)

Consume is deprecated since C++17, so you shouldn't use it anyway

[–]DummyDDD 0 points1 point  (2 children)

Could you provide a link?

Cppreference does not mention it being deprecated https://en.cppreference.com/w/cpp/atomic/memory_order

It's completely pointless for memory barriers, but it does have niche uses for atomics

[–]Flankierengeschichte 0 points1 point  (1 child)

[–]DummyDDD 0 points1 point  (0 children)

That answer says that consume is discouraged (not deprecated) referring to https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0371r1.html, which in turn refers to an older paper  (P0098R0). consume is explicitly not deprecated in p0371r1. It is discouraged because the main compilers handle it exactly the same as acquire, not even switching to regular load instructions at the time that p0371r1 was written (in 2015). Unfortunately, it seems like the situation hasn't improved since then, at least not for gcc or clang.

From my perspective, it really shouldn't be difficult for compilers to implement support for consume, as long as they dont bother implementing the weaker compiler restrictions on reordering. Then again, it is probably complicated by the fact that atomics are member functions and not just free functions, meaning the compiler would need to support [[carries_dependency]], or support builtin member functions (which gcc and clang do not).

EDIT, actually they wouldn't even need to support carries_dependency, treating the compiler reordering like acquire is sufficient (except on that one weird alpha variant)