Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
async/dispatch.hpp
Go to the documentation of this file.
1#pragma once
2
12
13#include <array>
14#include <optional>
15#include <span>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19
20namespace uni20::linalg
21{
22namespace detail
23{
24
25template <class Backend, class Op, class... Args> consteval bool backend_has_kernel_task_factory()
26{
27 if constexpr (requires {
28 try_make_kernel_task(std::declval<Backend const&>(), std::declval<Op const&>(),
29 std::declval<kernel_type_probe_arg_t<Args>>()...);
30 })
31 {
32 using result_type = decltype(try_make_kernel_task(std::declval<Backend const&>(), std::declval<Op const&>(),
33 std::declval<kernel_type_probe_arg_t<Args>>()...));
34 static_assert(is_kernel_task_attempt<result_type>,
35 "try_make_kernel_task must return KernelTaskAttempt<ConcreteTask>");
36 return true;
37 }
38 return false;
39}
40
41template <std::size_t Index, class... Backends, class Op, std::size_t ResultCount, class... Args>
42async::AsyncTask co_dispatch_kernel_at(backend_list<Backends...> const& backends, Op const& op,
43 std::array<std::optional<KernelAttempt>, ResultCount>& runtime_results,
44 bool& succeeded, Args&... args)
45{
46 static_assert(ResultCount == sizeof...(Backends));
47 if constexpr (Index == sizeof...(Backends))
48 {
49 co_return;
50 }
51 else
52 {
53 using backend_type = std::tuple_element_t<Index, std::tuple<Backends...>>;
54 constexpr auto acceptance = backend_type_acceptance<backend_type, Op, Args&...>();
55
56 if constexpr (acceptance != KernelTypeAcceptance::no)
57 {
58 KernelAttempt attempt;
59 if constexpr (backend_has_kernel_task_factory<backend_type, Op, Args&...>())
60 {
61 auto task_attempt = try_make_kernel_task(std::get<Index>(backends.entries), op, args...);
62 attempt = task_attempt.attempt();
63 runtime_results[Index] = attempt;
64 if (kernel_attempt_succeeded(attempt) && task_attempt.has_task())
65 {
66 co_await std::move(task_attempt).take_task();
67 }
68 }
69 else
70 {
71 attempt = try_kernel(std::get<Index>(backends.entries), op, args...);
72 runtime_results[Index] = attempt;
73 }
74
75 if constexpr (acceptance == KernelTypeAcceptance::yes)
76 {
78 }
79
80 if (kernel_attempt_succeeded(attempt))
81 {
82 succeeded = true;
83 co_return;
84 }
85 }
86
87 co_await co_dispatch_kernel_at<Index + 1>(backends, op, runtime_results, succeeded, args...);
88 }
89}
90
91} // namespace detail
92
104template <class BackendSelector, class Op, class... Args>
105 requires detail::KernelDispatchTypesAccepted<detail::normalized_backend_selector_t<BackendSelector>, Op, Args&...>
106async::AsyncTask co_dispatch_kernel(BackendSelector selector, Op op, Args&... args)
107{
108 auto backends = normalize_backend_selector(std::move(selector));
109 constexpr auto backend_count = std::tuple_size_v<decltype(backends.entries)>;
110 std::array<std::optional<KernelAttempt>, backend_count> runtime_results{};
111 bool succeeded = false;
112
113 co_await detail::co_dispatch_kernel_at<0>(backends, op, runtime_results, succeeded, args...);
114
115 auto const results = std::span<std::optional<KernelAttempt> const>(runtime_results);
116 if (succeeded)
117 {
119 {
120 auto diagnostic = detail::make_kernel_dispatch_diagnostic(backends, results, op, args...);
121 dispatch_diagnostics::detail::emit(diagnostic);
122 }
123 co_return;
124 }
125
127 {
128 auto diagnostic = detail::make_kernel_dispatch_diagnostic(backends, results, op, args...);
129 dispatch_diagnostics::detail::emit(diagnostic);
131 std::move(diagnostic.backend_attempts)));
132 }
133
134 trace::raise(detail::make_kernel_dispatch_error<KernelDispatchFailure::all_candidates_declined>(backends, results, op,
135 args...));
136}
137
138} // namespace uni20::linalg
Promise-neutral coroutine task ownership and the public AsyncTask type.
Canonical host-oriented coroutine task type.
Definition async_task.hpp:196
Exception carrying structured information about a failed backend walk.
Definition dispatch_error.hpp:93
Operation-tag backend dispatch over tensor-view operands.
Deferred task result for an optional coroutine kernel implementation.
void raise(Exception &&exception, std::source_location where=std::source_location::current())
Raise a concrete exception through Uni20's configured error boundary.
Definition trace_impl.hpp:2184
bool enabled() noexcept
Return whether runtime kernel-dispatch diagnostics are enabled.
Definition dispatch_diagnostics.hpp:60
Definition linalg/async.hpp:28
constexpr bool kernel_attempt_succeeded(KernelAttempt attempt) noexcept
Return whether a backend performed the operation.
Definition kernel_attempt.hpp:40
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr auto normalize_backend_selector(backend_list< Backends... > selector) -> backend_list< Backends... >
Return an explicit backend list unchanged.
Definition backend_selector.hpp:259
async::AsyncTask co_dispatch_kernel(BackendSelector selector, Op op, Args &... args)
Dispatch a kernel from a coroutine without requiring coroutine wrappers for blocking backends.
Definition async/dispatch.hpp:106
auto try_make_kernel_task(CublasBackend, gemm_op const &, OutputMdspan &output, Scalar const &alpha, LhsMdspan &lhs, RhsMdspan &rhs, Scalar const &beta) -> KernelTaskAttempt< async::CudaTask >
Prepare asynchronous cuBLAS work from normalized mdspecs.
Definition gemm_task.hpp:59
#define CHECK(cond,...)
Definition trace.hpp:299