Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P2897R7 aligned_accessor: An mdspan accessor expressing pointer over-alignment #7467

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading