Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
backend_selector.hpp
Go to the documentation of this file.
1#pragma once
2
9#include <concepts>
10#include <string_view>
11#include <tuple>
12#include <type_traits>
13#include <utility>
14
15namespace uni20::linalg
16{
17
20{
21 static constexpr std::string_view name = "lapack";
22 friend constexpr bool operator==(LapackBackend const&, LapackBackend const&) = default;
23};
24
27{
28 static constexpr std::string_view name = "blas";
29 friend constexpr bool operator==(BlasBackend const&, BlasBackend const&) = default;
30};
31
34{
35 static constexpr std::string_view name = "cublas";
36 friend constexpr bool operator==(CublasBackend const&, CublasBackend const&) = default;
37};
38
41{
42 static constexpr std::string_view name = "cusolver";
43 friend constexpr bool operator==(CusolverBackend const&, CusolverBackend const&) = default;
44};
45
48{
49 static constexpr std::string_view name = "cuda_reference";
50 friend constexpr bool operator==(CudaReferenceBackend const&, CudaReferenceBackend const&) = default;
51};
52
55{
56 static constexpr std::string_view name = "cpu_reference";
57 friend constexpr bool operator==(CpuReferenceBackend const&, CpuReferenceBackend const&) = default;
58};
59
61template <class... Backends> struct backend_list
62{
63 std::tuple<Backends...> entries;
64
65 constexpr explicit backend_list(Backends... backends) : entries(std::move(backends)...) {}
66
67 friend constexpr bool operator==(backend_list const&, backend_list const&) = default;
68};
69
70template <class... Backends> backend_list(Backends...) -> backend_list<std::remove_cvref_t<Backends>...>;
71
72template <class T> struct is_backend_list : std::false_type
73{};
74
75template <class... Backends> struct is_backend_list<backend_list<Backends...>> : std::true_type
76{};
77
78template <class T> inline constexpr bool is_backend_list_v = is_backend_list<std::remove_cvref_t<T>>::value;
79
81template <class T>
82concept KernelBackend = requires {
83 { std::remove_cvref_t<T>::name } -> std::convertible_to<std::string_view>;
84};
85
87template <class T>
88concept KernelBackendSelector = KernelBackend<T> || is_backend_list_v<T>;
89
95template <class Operation, class StoragePolicy> struct backend_selector_override
96{};
97
104template <class Operation, class StoragePolicy> struct backend_selector_default
105{};
106
112template <class StoragePolicy> inline constexpr bool enable_backend_neutral_storage = false;
113
114namespace detail
115{
116template <class Tensor>
117concept HasStoragePolicy = requires { typename std::remove_cvref_t<Tensor>::storage_policy; } &&
118 (!std::same_as<typename std::remove_cvref_t<Tensor>::storage_policy, void>);
119
120template <class Provider>
121concept HasStaticBackendSelector = requires { std::remove_cvref_t<Provider>::backend_selector(); };
122
123template <class Tensor> using tensor_storage_policy_t = typename std::remove_cvref_t<Tensor>::storage_policy;
124
125template <class... StoragePolicies> struct FirstBackendBoundStorage
126{
127 using type = void;
128};
129
130template <class StoragePolicy, class... Rest> struct FirstBackendBoundStorage<StoragePolicy, Rest...>
131{
132 using type = std::conditional_t<enable_backend_neutral_storage<StoragePolicy>,
133 typename FirstBackendBoundStorage<Rest...>::type, StoragePolicy>;
134};
135
136template <class... Tensors>
137using first_backend_bound_storage_t = typename FirstBackendBoundStorage<tensor_storage_policy_t<Tensors>...>::type;
138
139template <class StoragePolicy, class... Tensors>
140inline constexpr bool tensors_have_compatible_storage =
141 ((enable_backend_neutral_storage<tensor_storage_policy_t<Tensors>> ||
142 std::same_as<StoragePolicy, tensor_storage_policy_t<Tensors>>) &&
143 ...);
144
145template <class StoragePolicy, class Operation>
146[[nodiscard]] constexpr auto select_backend_for_storage(Operation const& operation)
147{
148 using operation_type = std::remove_cvref_t<Operation>;
150 if constexpr (requires { override_type::select(operation); })
151 {
152 auto selector = override_type::select(operation);
153 static_assert(KernelBackendSelector<decltype(selector)>,
154 "backend_selector_override::select must return a backend selector");
155 return selector;
156 }
157 else
158 {
159 auto storage_selector = StoragePolicy::backend_selector();
161 if constexpr (requires { default_type::select(operation, std::move(storage_selector)); })
162 {
163 auto selector = default_type::select(operation, std::move(storage_selector));
164 static_assert(KernelBackendSelector<decltype(selector)>,
165 "backend_selector_default::select must return a backend selector");
166 return selector;
167 }
168 else
169 {
170 static_assert(KernelBackendSelector<decltype(storage_selector)>,
171 "storage policy backend_selector must return a backend selector");
172 return storage_selector;
173 }
174 }
175}
176} // namespace detail
177
186template <class StoragePolicy, class Operation>
187[[nodiscard]] constexpr auto select_backend_for_storage(Operation const& operation)
188{
189 return detail::select_backend_for_storage<StoragePolicy>(operation);
190}
191
200template <class FirstTensor, class... RestTensors, class Operation>
201[[nodiscard]] constexpr auto select_backend_for(Operation const& operation)
202{
203 if constexpr (detail::HasStoragePolicy<FirstTensor> && (detail::HasStoragePolicy<RestTensors> && ...))
204 {
205 using storage_policy = detail::first_backend_bound_storage_t<FirstTensor, RestTensors...>;
206 if constexpr (std::is_void_v<storage_policy>)
207 {
208 using first_storage = detail::tensor_storage_policy_t<FirstTensor>;
209 static_assert(detail::HasStaticBackendSelector<first_storage> &&
210 (detail::HasStaticBackendSelector<detail::tensor_storage_policy_t<RestTensors>> && ...),
211 "backend-neutral storage policies must provide a static backend_selector()");
212 using selector_type = std::remove_cvref_t<decltype(first_storage::backend_selector())>;
213 static_assert(
214 (std::same_as<
215 selector_type,
216 std::remove_cvref_t<decltype(detail::tensor_storage_policy_t<RestTensors>::backend_selector())>> &&
217 ...),
218 "backend-neutral storage policies must provide compatible default backend selector types");
219 return detail::select_backend_for_storage<first_storage>(operation);
220 }
221 else
222 {
223 static_assert(
224 detail::tensors_have_compatible_storage<storage_policy, FirstTensor, RestTensors...>,
225 "tensor operands with different backend-bound storage policies require an explicit selector or transfer");
226 static_assert(detail::HasStaticBackendSelector<storage_policy>,
227 "tensor storage policy must provide a static backend_selector()");
228
229 return detail::select_backend_for_storage<storage_policy>(operation);
230 }
231 }
232 else
233 {
234 using first_tensor = std::remove_cvref_t<FirstTensor>;
235 static_assert(detail::HasStaticBackendSelector<first_tensor> &&
236 (detail::HasStaticBackendSelector<std::remove_cvref_t<RestTensors>> && ...),
237 "tensor operands without storage policies must provide a static backend_selector()");
238 using selector_type = std::remove_cvref_t<decltype(first_tensor::backend_selector())>;
239 static_assert((std::same_as<selector_type,
240 std::remove_cvref_t<decltype(std::remove_cvref_t<RestTensors>::backend_selector())>> &&
241 ...),
242 "tensor operands must provide compatible default backend selector types");
243 return first_tensor::backend_selector();
244 }
245}
246
251template <class Operation, class FirstTensor, class... RestTensors>
252[[nodiscard]] constexpr auto select_backend(Operation const& operation, FirstTensor const&, RestTensors const&...)
253{
254 return select_backend_for<FirstTensor, RestTensors...>(operation);
255}
256
258template <class... Backends>
259[[nodiscard]] constexpr auto normalize_backend_selector(backend_list<Backends...> selector) -> backend_list<Backends...>
260{
261 return selector;
262}
263
265template <class Backend>
266 requires(!is_backend_list_v<Backend>)
267[[nodiscard]] constexpr auto normalize_backend_selector(Backend&& backend)
268{
269 return backend_list{std::forward<Backend>(backend)};
270}
271
272} // namespace uni20::linalg
Single named backend or ordered backend list accepted by dispatch front ends.
Definition backend_selector.hpp:88
Named backend value accepted as one dispatch candidate.
Definition backend_selector.hpp:82
locale_t value
Definition data_table.cpp:49
Definition qnum.hpp:403
Definition linalg/async.hpp:28
constexpr auto select_backend_for(Operation const &operation)
Resolve the immutable backend selector for Tensor operand types.
Definition backend_selector.hpp:201
constexpr bool enable_backend_neutral_storage
Opt-in marker for storage policies that do not constrain backend selection.
Definition backend_selector.hpp:112
constexpr bool is_backend_list_v
Definition backend_selector.hpp:78
constexpr auto normalize_backend_selector(backend_list< Backends... > selector) -> backend_list< Backends... >
Return an explicit backend list unchanged.
Definition backend_selector.hpp:259
constexpr auto select_backend_for_storage(Operation const &operation)
Resolve a backend selector directly from one storage policy.
Definition backend_selector.hpp:187
constexpr auto select_backend(Operation const &operation, FirstTensor const &, RestTensors const &...)
Select the default backend list for an operation on Tensor operands.
Definition backend_selector.hpp:252
typename detail::TensorStoragePolicy< std::remove_cvref_t< Tensor > >::type tensor_storage_policy_t
Storage-policy type exposed by a tensor-level object, or void.
Definition tensor/concepts.hpp:40
Backend family for BLAS dense linalg kernels, including MPLAPACK scalar adapters.
Definition backend_selector.hpp:27
static constexpr std::string_view name
Definition backend_selector.hpp:28
friend constexpr bool operator==(BlasBackend const &, BlasBackend const &)=default
Backend value for the reference host CPU dense linalg oracle.
Definition backend_selector.hpp:55
static constexpr std::string_view name
Definition backend_selector.hpp:56
friend constexpr bool operator==(CpuReferenceBackend const &, CpuReferenceBackend const &)=default
Backend value for cuBLAS dense linalg kernels.
Definition backend_selector.hpp:34
static constexpr std::string_view name
Definition backend_selector.hpp:35
friend constexpr bool operator==(CublasBackend const &, CublasBackend const &)=default
Backend value for generic CUDA runtime kernels and transfers.
Definition backend_selector.hpp:48
friend constexpr bool operator==(CudaReferenceBackend const &, CudaReferenceBackend const &)=default
static constexpr std::string_view name
Definition backend_selector.hpp:49
Backend value for cuSOLVER dense factorization kernels.
Definition backend_selector.hpp:41
friend constexpr bool operator==(CusolverBackend const &, CusolverBackend const &)=default
static constexpr std::string_view name
Definition backend_selector.hpp:42
Backend family for configured dense LAPACK kernels, including MPLAPACK scalar adapters.
Definition backend_selector.hpp:20
friend constexpr bool operator==(LapackBackend const &, LapackBackend const &)=default
static constexpr std::string_view name
Definition backend_selector.hpp:21
Ordered list of backend values tried by the dispatch walk.
Definition backend_selector.hpp:62
constexpr backend_list(Backends... backends)
Definition backend_selector.hpp:65
friend constexpr bool operator==(backend_list const &, backend_list const &)=default
std::tuple< Backends... > entries
Definition backend_selector.hpp:63
Uni20-owned operation-specific default for a storage policy.
Definition backend_selector.hpp:105
Optional global backend-selector override for an operation and storage policy.
Definition backend_selector.hpp:96
Definition backend_selector.hpp:73