Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
elementwise_plan.hpp
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <concepts>
14#include <cstddef>
15#include <cstdint>
16#include <limits>
17#include <type_traits>
18#include <utility>
19
20namespace uni20::linalg::detail::cuda_reference
21{
22
24enum class ElementwiseIndexKind
25{
26 index_32,
27 index_64
28};
29
38template <std::size_t OperandCount, std::size_t MaximumRank, std::unsigned_integral LogicalIndex,
39 std::signed_integral Offset>
40struct StridedElementwisePlan
41{
42 using logical_index_type = LogicalIndex;
43 using offset_type = Offset;
44 using offsets_type = std::array<offset_type, OperandCount>;
45
46 static constexpr std::size_t operand_count = OperandCount;
47 static constexpr std::size_t maximum_rank = MaximumRank;
48 static_assert(MaximumRank <= std::numeric_limits<std::uint8_t>::max());
49
50 logical_index_type element_count = 0;
51 std::uint8_t compact_rank = 0;
52 std::array<logical_index_type, MaximumRank> extents{};
53 std::array<offsets_type, MaximumRank> strides{};
54 offsets_type base_offsets{};
55
57 [[nodiscard]] UNI20_HOST_DEVICE constexpr offsets_type offsets(logical_index_type logical_index) const noexcept
58 {
59 auto result = base_offsets;
60 for (std::size_t dimension = 0; dimension < compact_rank; ++dimension)
61 {
62 auto const extent = extents[dimension];
63 auto const coordinate = logical_index % extent;
64 logical_index /= extent;
65 for (std::size_t operand = 0; operand < OperandCount; ++operand)
66 {
67 auto const stride = strides[dimension][operand];
68 if (stride != 0) result[operand] += static_cast<offset_type>(coordinate) * stride;
69 }
70 }
71 return result;
72 }
73};
74
75template <std::size_t OperandCount, std::size_t MaximumRank>
76using StridedElementwisePlan32 = StridedElementwisePlan<OperandCount, MaximumRank, std::uint32_t, std::int32_t>;
77
78template <std::size_t OperandCount, std::size_t MaximumRank>
79using StridedElementwisePlan64 = StridedElementwisePlan<OperandCount, MaximumRank, std::uint64_t, std::int64_t>;
80
83template <std::size_t OperandCount, std::size_t MaximumRank> struct LoweredStridedElementwisePlan
84{
85 ElementwiseIndexKind index_kind = ElementwiseIndexKind::index_32;
86 StridedElementwisePlan32<OperandCount, MaximumRank> plan_32;
87 StridedElementwisePlan64<OperandCount, MaximumRank> plan_64;
88
90 [[nodiscard]] std::size_t element_count() const noexcept
91 {
92 if (index_kind == ElementwiseIndexKind::index_32) return plan_32.element_count;
93 return static_cast<std::size_t>(plan_64.element_count);
94 }
95
99 template <class Visitor> decltype(auto) visit(Visitor&& visitor) const
100 {
101 if (index_kind == ElementwiseIndexKind::index_32) return std::forward<Visitor>(visitor)(plan_32);
102 return std::forward<Visitor>(visitor)(plan_64);
103 }
104};
105
106namespace elementwise_plan_detail
107{
108
109template <class Target, class Source> [[nodiscard]] constexpr bool in_range(Source value) noexcept
110{
111 return std::in_range<Target>(value);
112}
113
114template <class LogicalIndex, class Offset, std::size_t OperandCount, std::size_t MaximumRank, std::size_t SourceRank>
115[[nodiscard]] bool try_lower(multi_iteration_plan<OperandCount, SourceRank> const& source,
116 StridedElementwisePlan<OperandCount, MaximumRank, LogicalIndex, Offset>& destination)
117{
118 constexpr auto maximum_logical_index = std::numeric_limits<std::make_signed_t<LogicalIndex>>::max();
119 if (!source.representable || source.rank() > MaximumRank ||
120 std::cmp_greater(source.element_count, maximum_logical_index))
121 return false;
122
123 destination.element_count = static_cast<LogicalIndex>(source.element_count);
124 if (source.empty())
125 {
126 destination.compact_rank = 0;
127 return true;
128 }
129 destination.compact_rank = static_cast<std::uint8_t>(source.rank());
130
131 for (std::size_t operand = 0; operand < OperandCount; ++operand)
132 {
133 auto const range = source.reachable_offsets[operand];
134 if ((!range.empty() && (range.minimum < 0 || !in_range<Offset>(range.maximum))) ||
135 source.base_offsets[operand] < 0 || !in_range<Offset>(source.base_offsets[operand]))
136 return false;
137 destination.base_offsets[operand] = static_cast<Offset>(source.base_offsets[operand]);
138 }
139
140 for (std::size_t device_dimension = 0; device_dimension < source.rank(); ++device_dimension)
141 {
142 auto const& source_dimension = source.dimensions[source.rank() - device_dimension - 1];
143 if (source_dimension.extent <= 0 || !in_range<LogicalIndex>(source_dimension.extent)) return false;
144 destination.extents[device_dimension] = static_cast<LogicalIndex>(source_dimension.extent);
145
146 for (std::size_t operand = 0; operand < OperandCount; ++operand)
147 {
148 auto const stride = source_dimension.strides[operand];
149 if (stride < 0 || !in_range<Offset>(stride)) return false;
150 destination.strides[device_dimension][operand] = static_cast<Offset>(stride);
151 }
152 }
153
154 return true;
155}
156
157} // namespace elementwise_plan_detail
158
166template <std::size_t MaximumRank, std::size_t OperandCount, std::size_t SourceRank>
167[[nodiscard]] bool
168try_lower_strided_elementwise_plan(multi_iteration_plan<OperandCount, SourceRank> const& source,
169 LoweredStridedElementwisePlan<OperandCount, MaximumRank>& destination)
170{
171 StridedElementwisePlan32<OperandCount, MaximumRank> plan_32;
172 if (elementwise_plan_detail::try_lower(source, plan_32))
173 {
174 destination.index_kind = ElementwiseIndexKind::index_32;
175 destination.plan_32 = plan_32;
176 return true;
177 }
178
179 StridedElementwisePlan64<OperandCount, MaximumRank> plan_64;
180 if (!elementwise_plan_detail::try_lower(source, plan_64)) return false;
181 destination.index_kind = ElementwiseIndexKind::index_64;
182 destination.plan_64 = plan_64;
183 return true;
184}
185
186} // namespace uni20::linalg::detail::cuda_reference
Project compiler annotations shared by host and device code.
#define UNI20_HOST_DEVICE
Definition compiler_attributes.hpp:12
locale_t value
Definition data_table.cpp:49
auto strides(S const &s)
Retrieve the strides associated with a strided mdspec-like type.
Definition mdspan/concepts.hpp:558
Helpers for constructing merged iteration plans over strided tensors.