Skip to content

Commit

Permalink
P2897R7 aligned_accessor: An mdspan accessor expressing pointer over-…
Browse files Browse the repository at this point in the history
…alignment
  • Loading branch information
jensmaurer authored and tkoeppe committed Dec 16, 2024
1 parent 6c6bd99 commit 8aa0ee9
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 0 deletions.
193 changes: 193 additions & 0 deletions source/containers.tex
Original file line number Diff line number Diff line change
Expand Up @@ -19779,6 +19779,10 @@
template<class ElementType>
class default_accessor;

// \ref{mdspan.accessor.aligned}, class template \tcode{aligned_accessor}
template<class ElementType, size_t ByteAlignment>
class aligned_accessor;

// \ref{mdspan.mdspan}, class template \tcode{mdspan}
template<class ElementType, class Extents, class LayoutPolicy = layout_right,
class AccessorPolicy = default_accessor<ElementType>>
Expand Down Expand Up @@ -23151,6 +23155,195 @@
Equivalent to: \tcode{return p + i;}
\end{itemdescr}

\rSec4[mdspan.accessor.aligned]{Class template \tcode{aligned_accessor}}

\rSec5[mdspan.accessor.aligned.overview]{Overview}

\begin{codeblock}
namespace std {
template<class ElementType, size_t ByteAlignment>
struct @\libglobal{aligned_accessor}@ {
using offset_policy = default_accessor<ElementType>;
using element_type = ElementType;
using reference = ElementType&;
using data_handle_type = ElementType*;

static constexpr size_t byte_alignment = ByteAlignment;

constexpr aligned_accessor() noexcept = default;
template<class OtherElementType, size_t OtherByteAlignment>
constexpr aligned_accessor(
aligned_accessor<OtherElementType, OtherByteAlignment>) noexcept;
template<class OtherElementType>
constexpr explicit aligned_accessor(default_accessor<OtherElementType>) noexcept;

template<class OtherElementType>
constexpr operator default_accessor<OtherElementType>() const noexcept;

constexpr reference access(data_handle_type p, size_t i) const noexcept;

constexpr typename offset_policy::data_handle_type offset(
data_handle_type p, size_t i) const noexcept;
};
}
\end{codeblock}

\pnum
\mandates
\begin{itemize}
\item \tcode{byte_alignment} is a power of two, and
\item \tcode{byte_alignment >= alignof(ElementType)} is \tcode{true}.
\end{itemize}

\pnum
\tcode{aligned_accessor} meets the accessor policy requirements.

\pnum
\tcode{ElementType} is required to be a complete object type
that is neither an abstract class type nor an array type.

\pnum
Each specialization of \tcode{aligned_accessor} is
a trivially copyable type that models \libconcept{semiregular}.

\pnum
\range{0}{$n$} is an accessible range
for an object \tcode{p} of type \tcode{data_handle_type} and
an object of type \tcode{aligned_accessor} if and only if
\begin{itemize}
\item
\range{p}{p + $n$} is a valid range, and,
\item
if $n$ is greater than zero,
then \tcode{is_sufficiently_aligned<byte_alignment>(p)} is \tcode{true}.
\end{itemize}

\pnum
\begin{example}
The following function \tcode{compute}
uses \tcode{is_sufficiently_aligned} to check
whether a given \tcode{mdspan} with \tcode{default_accessor} has
a data handle with sufficient alignment
to be used with \tcode{aligned_accessor<float, 4 * sizeof(float)>}.
If so, the function dispatches to
a function \tcode{compute_using_fourfold_overalignment}
that requires fourfold over-alignment of arrays,
but can therefore use hardware-specific instructions,
such as four-wide SIMD (Single Instruction Multiple Data) instructions.
Otherwise, \tcode{compute} dispatches to a
possibly less optimized function \tcode{compute_without_requiring_overalignment}
that has no over-alignment requirement.
\begin{codeblock}
void compute_using_fourfold_overalignment(
std::mdspan<float, std::dims<1>, std::layout_right,
std::aligned_accessor<float, 4 * alignof(float)>> x);

void compute_without_requiring_overalignment(
std::mdspan<float, std::dims<1>, std::layout_right> x);

void compute(std::mdspan<float, std::dims<1>> x) {
constexpr auto byte_alignment = 4 * sizeof(float);
auto accessor = std::aligned_accessor<float, byte_alignment>{};
auto x_handle = x.data_handle();

if (std::is_sufficiently_aligned<byte_alignment>(x_handle)) {
compute_using_fourfold_overalignment(std::mdspan{x_handle, x.mapping(), accessor});
} else {
compute_without_requiring_overalignment(x);
}
}
\end{codeblock}
\end{example}

\rSec5[mdspan.accessor.aligned.members]{Members}

\indexlibraryctor{aligned_accessor}%
\begin{itemdecl}
template<class OtherElementType, size_t OtherByteAlignment>
constexpr aligned_accessor(aligned_accessor<OtherElementType, OtherByteAlignment>) noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\constraints
\begin{itemize}
\item
\tcode{is_convertible_v<OtherElementType(*)[], element_type(*)[]>}
is \tcode{true}.
\item
\tcode{OtherByteAlignment >= byte_alignment} is \tcode{true}.
\end{itemize}

\pnum
\effects
None.
\end{itemdescr}

\indexlibraryctor{aligned_accessor}%
\begin{itemdecl}
template<class OtherElementType>
constexpr explicit aligned_accessor(default_accessor<OtherElementType>) noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\constraints
\tcode{is_convertible_v<OtherElementType(*)[], element_type(*)[]>}
is \tcode{true}.

\pnum
\effects
None.
\end{itemdescr}

\indexlibrarymember{access}{aligned_accessor}%
\begin{itemdecl}
constexpr reference access(data_handle_type p, size_t i) const noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\expects
\range{0}{i + 1} is an accessible range for \tcode{p} and \tcode{*this}.

\pnum
\effects
Equivalent to: \tcode{return assume_aligned<byte_alignment>(p)[i];}
\end{itemdescr}

\indexlibrarymember{operator default_accessor}{aligned_accessor}%
\begin{itemdecl}
template<class OtherElementType>
constexpr operator default_accessor<OtherElementType>() const noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\constraints
\tcode{is_convertible_v<element_type(*)[], OtherElementType(*)[]>}
is \tcode{true}.

\pnum
\effects
Equivalent to: \tcode{return \{\};}
\end{itemdescr}

\indexlibrarymember{offset}{aligned_accessor}%
\begin{itemdecl}
constexpr typename offset_policy::data_handle_type
offset(data_handle_type p, size_t i) const noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\expects
\range{0}{i + 1} is an accessible range for \tcode{p} and \tcode{*this}.

\pnum
\effects
Equivalent to: \tcode{return assume_aligned<byte_alignment>(p) + i;}
\end{itemdescr}

\rSec3[mdspan.mdspan]{Class template \tcode{mdspan}}

\rSec4[mdspan.mdspan.overview]{Overview}
Expand Down
24 changes: 24 additions & 0 deletions source/memory.tex
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
void* align(size_t alignment, size_t size, void*& ptr, size_t& space); // freestanding
template<size_t N, class T>
constexpr T* assume_aligned(T* ptr); // freestanding
template<size_t Alignment, class T>
bool is_sufficiently_aligned(T* ptr);

// \ref{obj.lifetime}, explicit lifetime management
template<class T>
Expand Down Expand Up @@ -864,6 +866,28 @@
\end{note}
\end{itemdescr}

\indexlibraryglobal{is_sufficiently_aligned}%
\begin{itemdecl}
template<size_t Alignment, class T>
bool is_sufficiently_aligned(T* ptr);
\end{itemdecl}

\begin{itemdescr}
\pnum
\expects
\tcode{p} points to
an object \tcode{X} of a type similar\iref{conv.qual} to \tcode{T}.

\pnum
\returns
\tcode{true} if \tcode{X} has alignment at least \tcode{Alignment},
otherwise \tcode{false}.

\pnum
\throws
Nothing.
\end{itemdescr}

\rSec2[obj.lifetime]{Explicit lifetime management}

\indexlibraryglobal{start_lifetime_as}%
Expand Down
2 changes: 2 additions & 0 deletions source/support.tex
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@
// also in \libheader{algorithm}, \libheader{ranges}, \libheader{string}, \libheader{deque}, \libheader{list}, \libheader{forward_list}, \libheader{vector}
#define @\defnlibxname{cpp_lib_algorithm_iterator_requirements}@ 202207L
// also in \libheader{algorithm}, \libheader{numeric}, \libheader{memory}
#define @\defnlibxname{cpp_lib_aligned_accessor}@ 202411L // also in \libheader{mdspan}
#define @\defnlibxname{cpp_lib_allocate_at_least}@ 202302L // also in \libheader{memory}
#define @\defnlibxname{cpp_lib_allocator_traits_is_always_equal}@ 201411L
// freestanding, also in \libheader{memory}, \libheader{scoped_allocator}, \libheader{string}, \libheader{deque}, \libheader{forward_list}, \libheader{list},
Expand Down Expand Up @@ -702,6 +703,7 @@
#define @\defnlibxname{cpp_lib_is_null_pointer}@ 201309L // freestanding, also in \libheader{type_traits}
#define @\defnlibxname{cpp_lib_is_pointer_interconvertible}@ 201907L // freestanding, also in \libheader{type_traits}
#define @\defnlibxname{cpp_lib_is_scoped_enum}@ 202011L // freestanding, also in \libheader{type_traits}
#define @\defnlibxname{cpp_lib_is_sufficiently_aligned}@ 202411L // also in \libheader{memory}
#define @\defnlibxname{cpp_lib_is_swappable}@ 201603L // freestanding, also in \libheader{type_traits}
#define @\defnlibxname{cpp_lib_is_virtual_base_of}@ 202406L // freestanding, also in \libheader{type_traits}
#define @\defnlibxname{cpp_lib_is_within_lifetime}@ 202306L // freestanding, also in \libheader{type_traits}
Expand Down

0 comments on commit 8aa0ee9

Please sign in to comment.