-
Notifications
You must be signed in to change notification settings - Fork 25
Open
Description
Atomic Refs Bound to Memory Orderings & Atomic Accessors
The mdspan paper P0009 listed atomic accessors as a reason for having accessors in the first place.
One of the use cases is for parallel algorithms updating data in a way which has data races.
Consider the histogram computation:
template<class ExecT>
void compute_histogram(ExecT exec, float bin_size,
std::mdspan<int, std::dextents<size_t,1>> output,
std::mdspan<float, std::dextents<size_t,1>> data) {
static_assert(std::is_execution_policy_v<ExecT>);
std::for_each(exec, data.data_handle(), data.data_handle()+data.extent(0), [=](float val) {
int bin = std::abs(val)/bin_size;
bin = std::clamp(bin, size_t(0), output.extent(0));
output[bin]++;
});Depending on whether ExecT is sequenced_policy or not, the update needs to happen atomically.
With just atomic_ref one could do:
std::for_each(exec, data.data_handle(), data.data_handle()+data.extent(0), [=](float val) {
int bin = std::abs(val)/bin_size;
bin = std::clamp(bin, size_t(0), output.extent(0));
atomic_ref(output[bin])++;
});This paper proposes a way for doing that for general mdspans without writing out atomic_ref everywhere inside a complex algorithm and also fixes the fact that you can't do the simple ++ with relaxed memory order - enough for these kind of accumulation cases.
What this paper proposes
- "Bounded" versions of
atomic_refwith a fixed (bounded) memory orderatomic_ref_relaxed,atomic_ref_seq_cst,atomic_ref_acq_rel- Conceptually just like
atomic_refwith a fixed memory order - all the same members
- Corresponding atomic accessors which use the various
atomic_reftypes as reference typeatomic_accessor=>atomic_refatomic_accessor_relaxed=>atomic_ref_relaxedatomic_accessor_acq_rel=>atomic_ref_acq_relatomic_accessor_seq_cst=>atomic_ref_seq_cst
Histogram with atomic accessor
// sequenced_policy does not attach atomic accessor
template<class T, class Extents, class LayoutPolicy>
auto add_atomic_accessor_if_needed(
std::execution::sequenced_policy, mdspan<T, Extents, LayoutPolicy> m) {
return m;
}
// parallel policies attach atomic accessor:
template<class ExecutionPolicy, class T, class Extents, class LayoutPolicy>
auto add_atomic_accessor_if_needed(
ExecutionPolicy, mdspan<T, Extents, LayoutPolicy> m) {
return mdspan(m.data_handle(), m.mapping(), atomic_accessor<T>());
}
template<class ExecT>
void compute_histogram(ExecT exec, float bin_size,
std::mdspan<int, std::dextents<size_t,1>> output,
std::mdspan<float, std::dextents<size_t,1>> data) {
static_assert(std::is_execution_policy_v<ExecT>);
auto accumulator = add_atomic_accessor_if_needed(exec, output);
std::for_each(exec, data.data_handle(), data.data_handle()+data.extent(0), [=](float val) {
int bin = std::abs(val)/bin_size;
bin = std::clamp(bin, size_t(0), output.extent(0));
// this is now atomic for parallel policies
accumulator[bin]++;
});Previous Question: Why not expose the templated type?
- There is really no use-case where one would want to write something generic over the memory order!
- The algorithm determines the memory order you need - the only generic question we identified is whether you need atomics at all (i.e. what execution policy is used)
- SG1 also felt that its easier to see the memory order with the explicit names
- We don't feel there is any real risk here and
mdspansomewhat hides its accessor anyway
- We don't feel there is any real risk here and
Wording Considerations
- Used exposition only
atomic-ref-bound<T,MemoryOrder>for wordingatomic_ref_MEM_ORDERare template aliases - see open question whether this is ok
atomic_refhas 4 specializations: general, integral (not bool), floating point and pointeratomic-ref-boundneeds only 2 (general and arithmetic/pointer{not-bool} versions)- integral, floating point and pointer type versions can be defined in terms of
atomic_refwith constraints on member functions - the only reason we need general is
difference_typemember typedef -> doesn't exist inatomic_refgeneral
- integral, floating point and pointer type versions can be defined in terms of
Open Questions
- Is reasoning for having
atomic-ref-boundexposition only good enough, or does LEWG want it as a public thing?- Please Poll:
atomic-ref-boundshould be exposition only. - Author position: in favor
- Please Poll:
- Converting between
atomic-ref-boundandbasic-atomic-accessorsof different memory orders- We don't see many good reasons to have them:
atomic_refis meant to be almost usedin-placefor individual operations e.g.atomic_ref(a[i])++- atomic accessors are something you are supposed to add in a fairly local scope
- we don't expect folks to pass
mdspanwith atomic accessors around
- There is a reason not to permit it:
- Writing functions which take
atomic_ref_MEM_ORDERimply ordering behavior: allowing conversions would change that behavior
- Writing functions which take
- Please Poll:
atomic-ref-boundandbasic-atomic-accessorshould have converting constructors for different memory-orders- Author position: against
- We don't see many good reasons to have them:
Metadata
Metadata
Assignees
Labels
No labels