19namespace uni20::linalg::cpu::detail
22template <
bool ReadsOutput,
class Output,
class Operation,
class... Inputs>
consteval bool strided_transform_operation()
24 using output_type = std::remove_cvref_t<Output>;
26 !((output_type::rank() == std::remove_cvref_t<Inputs>::rank()) && ...))
30 else if constexpr (ReadsOutput)
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)
36 output = std::invoke(operation, output, std::declval<
typename std::remove_cvref_t<Inputs>::reference>()...);
41 return std::invocable<Operation&, typename std::remove_cvref_t<Inputs>::reference...>&&
42 requires(
typename output_type::reference output, Operation & operation)
44 output = std::invoke(operation, std::declval<
typename std::remove_cvref_t<Inputs>::reference>()...);
50class strided_transform_executor {
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;
56 using offset_type = std::array<index_type, span_count>;
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))
65 template <
class Plan>
void run(Plan
const& plan, offset_type offsets)
70 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 0>{});
73 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 1>{});
76 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 2>{});
79 this->run_unrolled(offsets, plan.data(), std::integral_constant<std::size_t, 3>{});
82 this->run_dynamic(offsets, plan.data(), plan.size());
88 void apply(offset_type
const& offsets)
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)
95 std::invoke(operation_, output,
96 std::get<Input + 1>(accessors_).access(std::get<Input + 1>(handles_), offsets[Input + 1])...);
100 output = std::invoke(
101 operation_, std::get<Input + 1>(accessors_).access(std::get<Input + 1>(handles_), offsets[Input + 1])...);
103 }(std::make_index_sequence<input_count>{});
106 template <std::
size_t Depth>
107 void run_unrolled(offset_type offsets, plan_entry
const* plan, std::integral_constant<std::size_t, Depth>)
109 if constexpr (Depth == 0)
111 this->apply(offsets);
116 for (
index_type index = 0; index < extent; ++index)
118 this->run_unrolled(offsets, plan + 1, std::integral_constant<std::size_t, Depth - 1>{});
120 offsets[
span] += plan->strides[
span];
125 void run_dynamic(offset_type offsets, plan_entry
const* plan, std::size_t depth)
127 constexpr std::size_t unroll_depth = 3;
129 for (
index_type index = 0; index < extent; ++index)
131 if (depth == unroll_depth + 1)
132 this->run_unrolled(offsets, plan + 1, std::integral_constant<std::size_t, 3>{});
134 this->run_dynamic(offsets, plan + 1, depth - 1);
137 offsets[
span] += plan->strides[
span];
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_;
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)
150 auto mappings = std::tuple{output.mapping(), inputs.mapping()...};
152 CHECK(plan.representable,
"elementwise iteration plan exceeds host index range");
154 strided_transform_executor<ReadsOutput, Operation, Output, Inputs...> executor{std::forward<Operation>(operation),
156 executor.run(plan.dimensions, plan.base_offsets);