Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
strided_transform.hpp
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <cstddef>
14#include <functional>
15#include <tuple>
16#include <type_traits>
17#include <utility>
18
19namespace uni20::linalg::cpu::detail
20{
21
22template <bool ReadsOutput, class Output, class Operation, class... Inputs> consteval bool strided_transform_operation()
23{
24 using output_type = std::remove_cvref_t<Output>;
25 if constexpr (!MutableStridedMdspanLike<Output> || (!(StridedMdspanLike<Inputs> && ...)) ||
26 !((output_type::rank() == std::remove_cvref_t<Inputs>::rank()) && ...))
27 {
28 return false;
29 }
30 else if constexpr (ReadsOutput)
31 {
32 return std::invocable<Operation&, typename output_type::reference,
33 typename std::remove_cvref_t<Inputs>::reference...>&&
34 requires(typename output_type::reference output, Operation & operation)
35 {
36 output = std::invoke(operation, output, std::declval<typename std::remove_cvref_t<Inputs>::reference>()...);
37 };
38 }
39 else
40 {
41 return std::invocable<Operation&, typename std::remove_cvref_t<Inputs>::reference...>&&
42 requires(typename output_type::reference output, Operation & operation)
43 {
44 output = std::invoke(operation, std::declval<typename std::remove_cvref_t<Inputs>::reference>()...);
45 };
46 }
47}
48
49template <bool ReadsOutput, class Operation, MutableStridedMdspanLike Output, StridedMdspanLike... Inputs>
50class strided_transform_executor {
51 public:
52 using operation_type = std::decay_t<Operation>;
53 static constexpr std::size_t input_count = sizeof...(Inputs);
54 static constexpr std::size_t span_count = 1 + input_count;
55 using index_type = std::ptrdiff_t;
56 using offset_type = std::array<index_type, span_count>;
57 using plan_entry = extent_strides<span_count>;
58
59 template <class FwdOperation>
60 strided_transform_executor(FwdOperation&& operation, Output const& output, Inputs const&... inputs)
61 : handles_{output.data_handle(), inputs.data_handle()...}, accessors_{output.accessor(), inputs.accessor()...},
62 operation_(std::forward<FwdOperation>(operation))
63 {}
64
65 template <class Plan> void run(Plan const& plan, offset_type offsets)
66 {
67 switch (plan.size())
68 {
69 case 0:
70 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 0>{});
71 return;
72 case 1:
73 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 1>{});
74 return;
75 case 2:
76 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 2>{});
77 return;
78 case 3:
79 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 3>{});
80 return;
81 default:
82 this->run_dynamic(offsets, plan.data(), plan.size());
83 return;
84 }
85 }
86
87 private:
88 void apply(offset_type const& offsets)
89 {
90 [&]<std::size_t... Input>(std::index_sequence<Input...>) {
91 auto&& output = std::get<0>(accessors_).access(std::get<0>(handles_), offsets[0]);
92 if constexpr (ReadsOutput)
93 {
94 output =
95 std::invoke(operation_, output,
96 std::get<Input + 1>(accessors_).access(std::get<Input + 1>(handles_), offsets[Input + 1])...);
97 }
98 else
99 {
100 output = std::invoke(
101 operation_, std::get<Input + 1>(accessors_).access(std::get<Input + 1>(handles_), offsets[Input + 1])...);
102 }
103 }(std::make_index_sequence<input_count>{});
104 }
105
106 template <std::size_t Depth>
107 void run_unrolled(offset_type offsets, plan_entry const* plan, std::integral_constant<std::size_t, Depth>)
108 {
109 if constexpr (Depth == 0)
110 {
111 this->apply(offsets);
112 }
113 else
114 {
115 index_type const extent = plan->extent;
116 for (index_type index = 0; index < extent; ++index)
117 {
118 this->run_unrolled(offsets, plan + 1, std::integral_constant<std::size_t, Depth - 1>{});
119 for (std::size_t span = 0; span < span_count; ++span)
120 offsets[span] += plan->strides[span];
121 }
122 }
123 }
124
125 void run_dynamic(offset_type offsets, plan_entry const* plan, std::size_t depth)
126 {
127 constexpr std::size_t unroll_depth = 3;
128 index_type const extent = plan->extent;
129 for (index_type index = 0; index < extent; ++index)
130 {
131 if (depth == unroll_depth + 1)
132 this->run_unrolled(offsets, plan + 1, std::integral_constant<std::size_t, 3>{});
133 else
134 this->run_dynamic(offsets, plan + 1, depth - 1);
135
136 for (std::size_t span = 0; span < span_count; ++span)
137 offsets[span] += plan->strides[span];
138 }
139 }
140
141 std::tuple<typename Output::data_handle_type, typename Inputs::data_handle_type...> handles_;
142 std::tuple<typename Output::accessor_type, typename Inputs::accessor_type...> accessors_;
143 [[no_unique_address]] operation_type operation_;
144};
145
146template <bool ReadsOutput, class Output, class Operation, class... Inputs>
147 requires(strided_transform_operation<ReadsOutput, Output, Operation, Inputs...>())
148void transform_strided(Output output, Operation&& operation, Inputs const&... inputs)
149{
150 auto mappings = std::tuple{output.mapping(), inputs.mapping()...};
151 auto plan = make_multi_iteration_plan(mappings);
152 CHECK(plan.representable, "elementwise iteration plan exceeds host index range");
153
154 strided_transform_executor<ReadsOutput, Operation, Output, Inputs...> executor{std::forward<Operation>(operation),
155 output, inputs...};
156 executor.run(plan.dimensions, plan.base_offsets);
157}
158
159} // namespace uni20::linalg::cpu::detail
Mutable mdspan-like types whose layout reports they are always strided.
Definition mdspan/concepts.hpp:421
An mdspan-like type whose mapping is always strided and exposes stride observers.
Definition mdspan/concepts.hpp:406
auto make_multi_iteration_plan(std::tuple< Mappings... > const &mappings)
Build a merged iteration plan for multiple tensors sharing the same extents.
Definition iteration_plan.hpp:263
Helpers for constructing merged iteration plans over strided tensors.
Mdspan concept and accessor extensions for Uni20.
std::ptrdiff_t index_type
Definition types.hpp:19
std::size_t span
Definition presentation.cpp:2017
Structure to represent a common extent and an array of strides.
Definition strides.hpp:28
#define CHECK(cond,...)
Definition trace.hpp:299