Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
async/matrix_product.hpp
Go to the documentation of this file.
1#pragma once
2
18
19#if UNI20_BACKEND_CUBLAS
21#endif
22
23#include <concepts>
24#include <memory>
25#include <tuple>
26#include <type_traits>
27#include <utility>
28
29namespace uni20::linalg
30{
31namespace detail
32{
33template <class OutputTensor, class LhsTensor, class RhsTensor>
34void validate_async_matrix_product_aliasing(async::Async<OutputTensor> const& output,
36{
37 auto const* const output_queue = std::addressof(output.queue());
38 ERROR_IF(output_queue == std::addressof(lhs.queue()) || output_queue == std::addressof(rhs.queue()),
39 "async matrix product output must not share an epoch queue with an input");
40}
41
42template <class BackendSelector, uni20::AsyncTensorOutput OutputTensor, uni20::RankedTensorView<2> LhsTensor,
43 uni20::RankedTensorView<2> RhsTensor, class AlphaAwaiter>
44async::AsyncTask co_assign_product(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
46 AlphaAwaiter alpha, std::optional<Precision> requested)
47{
48 auto output_awaiter = uni20::detail::mutable_async_tensor_awaiter(output);
49 auto awaited = co_await async::all(output_awaiter, lhs, rhs, alpha);
50 auto const& lhs_value = std::get<1>(awaited);
51 auto const& rhs_value = std::get<2>(awaited);
52 auto const& alpha_value = std::get<3>(awaited);
53
54 using scalar_type = uni20::tensor_element_t<OutputTensor>;
55 scalar_type const alpha_scalar = static_cast<scalar_type>(alpha_value);
56 auto lhs_descriptor = uni20::mdspec_of(lhs_value);
57 auto rhs_descriptor = uni20::mdspec_of(rhs_value);
58 auto p = requested ? *requested : common_default_precision(lhs_value, rhs_value);
59 require_precision<scalar_type>(p);
60#if UNI20_HAS_MPREAL
61 if constexpr (has_runtime_precision_v<scalar_type>)
62 {
63 if constexpr (async::is_async_alias_v<OutputTensor>)
64 {
65 auto output_value = uni20::detail::mutable_async_tensor_value<OutputTensor>(std::get<0>(awaited));
66 uni20::linalg::assign_product(selector, output_value, lhs_value, rhs_value, alpha_scalar, p);
67 }
68 else
69 {
70 auto& result = prepare_output(std::get<0>(awaited), matrix_product_shape(lhs_value, rhs_value), p);
71 uni20::linalg::assign_product(selector, result, lhs_value, rhs_value, alpha_scalar, p);
72 }
73 }
74 else
75#endif
76 if constexpr (async::is_async_alias_v<OutputTensor>)
77 {
78 auto output_value = uni20::detail::mutable_async_tensor_value<OutputTensor>(std::get<0>(awaited));
79 co_await co_dispatch_kernel(selector, assign_product_op{}, output_value, alpha_scalar, lhs_descriptor,
80 rhs_descriptor);
81 }
82 else
83 {
84 auto& storage = std::get<0>(awaited);
85 co_await co_dispatch_kernel(selector, assign_product_op{}, storage, alpha_scalar, lhs_descriptor, rhs_descriptor);
86 }
87 co_return;
88}
89
90template <class BackendSelector, uni20::AsyncTensorOutput OutputTensor, uni20::RankedTensorView<2> LhsTensor,
91 uni20::RankedTensorView<2> RhsTensor, class AlphaAwaiter, class BetaAwaiter>
92async::AsyncTask co_gemm(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
94 BetaAwaiter beta, std::optional<Precision> requested)
95{
96 auto output_awaiter = uni20::detail::mutable_async_tensor_awaiter(output);
97 auto awaited = co_await async::all(output_awaiter, lhs, rhs, alpha, beta);
98 decltype(auto) output_value = uni20::detail::mutable_async_tensor_value<OutputTensor>(std::get<0>(awaited));
99 auto const& lhs_value = std::get<1>(awaited);
100 auto const& rhs_value = std::get<2>(awaited);
101 auto const& alpha_value = std::get<3>(awaited);
102 auto const& beta_value = std::get<4>(awaited);
103
104 auto const shape = matrix_product_shape(lhs_value, rhs_value);
105 uni20::require_output(output_value, shape);
106 using scalar_type = uni20::tensor_element_t<OutputTensor>;
107 scalar_type const alpha_scalar = static_cast<scalar_type>(alpha_value);
108 scalar_type const beta_scalar = static_cast<scalar_type>(beta_value);
109 if (requested) require_precision<scalar_type>(*requested);
110 auto output_span = uni20::mdspec_of(output_value);
111 auto lhs_span = uni20::mdspec_of(lhs_value);
112 auto rhs_span = uni20::mdspec_of(rhs_value);
113#if UNI20_HAS_MPREAL
114 if constexpr (has_runtime_precision_v<scalar_type>)
115 uni20::linalg::gemm(selector, output_value, alpha_scalar, lhs_value, rhs_value, beta_scalar, requested);
116 else
117#endif
118 co_await co_dispatch_kernel(selector, gemm_op{}, output_span, alpha_scalar, lhs_span, rhs_span, beta_scalar);
119 co_return;
120}
121
122template <class BackendSelector, uni20::AsyncTensorOutput OutputTensor, uni20::RankedTensorView<2> LhsTensor,
123 uni20::RankedTensorView<2> RhsTensor, class AlphaAwaiter>
124void schedule_async_assign_product(BackendSelector selector, async::Async<OutputTensor>& output,
126 AlphaAwaiter alpha, std::optional<Precision> p = {})
127{
128 validate_async_matrix_product_aliasing(output, lhs, rhs);
129 auto task = co_assign_product(std::move(selector), output.write(), lhs.read(), rhs.read(), std::move(alpha), p);
130 task.debug_name("assign_product");
131 async::schedule(std::move(task));
132}
133
134template <class BackendSelector, uni20::AsyncTensorOutput OutputTensor, uni20::RankedTensorView<2> LhsTensor,
135 uni20::RankedTensorView<2> RhsTensor, class AlphaAwaiter, class BetaAwaiter>
136void schedule_async_gemm(BackendSelector selector, async::Async<OutputTensor>& output,
137 async::Async<LhsTensor> const& lhs, async::Async<RhsTensor> const& rhs, AlphaAwaiter alpha,
138 BetaAwaiter beta, std::optional<Precision> p = {})
139{
140 validate_async_matrix_product_aliasing(output, lhs, rhs);
141 auto task =
142 co_gemm(std::move(selector), output.write(), lhs.read(), rhs.read(), std::move(alpha), std::move(beta), p);
143 task.debug_name("gemm");
144 async::schedule(std::move(task));
145}
146} // namespace detail
147
156template <class BackendSelector, uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
157 uni20::RankedTensorView<2> RhsTensor, class Alpha, class Beta>
159 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
160 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>> &&
161 AsyncOperationScalar<Beta, uni20::tensor_element_t<OutputTensor>>
162void gemm(BackendSelector selector, async::Async<OutputTensor>& output, Alpha&& alpha,
163 async::Async<LhsTensor> const& lhs, async::Async<RhsTensor> const& rhs, Beta&& beta,
164 std::optional<Precision> p = {})
165{
166 detail::schedule_async_gemm(std::move(selector), output, lhs, rhs, async::read(std::forward<Alpha>(alpha)),
167 async::read(std::forward<Beta>(beta)), p);
168}
169
171template <uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
172 uni20::RankedTensorView<2> RhsTensor, class Alpha, class Beta>
174 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
175 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>> &&
176 AsyncOperationScalar<Beta, uni20::tensor_element_t<OutputTensor>>
177void gemm(async::Async<OutputTensor>& output, Alpha&& alpha, async::Async<LhsTensor> const& lhs,
178 async::Async<RhsTensor> const& rhs, Beta&& beta, std::optional<Precision> p = {})
179{
180 auto selector = select_backend_for<OutputTensor, LhsTensor, RhsTensor>(gemm_op{});
181 gemm(std::move(selector), output, std::forward<Alpha>(alpha), lhs, rhs, std::forward<Beta>(beta), p);
182}
183
193template <class BackendSelector, uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
194 uni20::RankedTensorView<2> RhsTensor, class Alpha = uni20::tensor_element_t<OutputTensor>>
196 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
197 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>>
198void assign_product(BackendSelector selector, async::Async<OutputTensor>& output, async::Async<LhsTensor> const& lhs,
199 async::Async<RhsTensor> const& rhs, Alpha&& alpha, std::optional<Precision> p = {})
200{
201 detail::schedule_async_assign_product(std::move(selector), output, lhs, rhs, async::read(std::forward<Alpha>(alpha)),
202 p);
203}
204
210template <uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
211 uni20::RankedTensorView<2> RhsTensor, class Alpha = uni20::tensor_element_t<OutputTensor>>
213 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
214 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>>
216 async::Async<RhsTensor> const& rhs, Alpha&& alpha, std::optional<Precision> p = {})
217{
218 auto selector = select_backend_for<OutputTensor, LhsTensor, RhsTensor>(assign_product_op{});
219 detail::schedule_async_assign_product(std::move(selector), output, lhs, rhs, async::read(std::forward<Alpha>(alpha)),
220 p);
221}
222
229template <class BackendSelector, uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
230 uni20::RankedTensorView<2> RhsTensor, class Alpha = uni20::tensor_element_t<OutputTensor>>
232 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
233 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>>
234void add_product(BackendSelector selector, async::Async<OutputTensor>& output, async::Async<LhsTensor> const& lhs,
235 async::Async<RhsTensor> const& rhs, Alpha&& alpha, std::optional<Precision> p = {})
236{
237 gemm(std::move(selector), output, std::forward<Alpha>(alpha), lhs, rhs, uni20::tensor_element_t<OutputTensor>{1}, p);
238}
239
245template <uni20::MutableRankedTensorView<2> OutputTensor, uni20::RankedTensorView<2> LhsTensor,
246 uni20::RankedTensorView<2> RhsTensor, class Alpha = uni20::tensor_element_t<OutputTensor>>
248 detail::CompatibleMatrixProductTensors<OutputTensor, LhsTensor, RhsTensor> &&
249 AsyncOperationScalar<Alpha, uni20::tensor_element_t<OutputTensor>>
251 async::Async<RhsTensor> const& rhs, Alpha&& alpha, std::optional<Precision> p = {})
252{
253 gemm(output, std::forward<Alpha>(alpha), lhs, rhs, uni20::tensor_element_t<OutputTensor>{1}, p);
254}
255
257template <KernelBackendSelector Selector, uni20::AsyncTensorOutput Output, RankedTensorView<2> A, RankedTensorView<2> B>
258 requires detail::CompatibleMatrixProductTensors<Output, A, B>
259void assign_product(Selector selector, async::Async<Output>& output, async::Async<A> const& a, async::Async<B> const& b,
260 std::optional<Precision> p = {})
261{
262 assign_product(std::move(selector), output, a, b, tensor_element_t<Output>{1}, p);
263}
264
266template <uni20::AsyncTensorOutput Output, RankedTensorView<2> A, RankedTensorView<2> B>
267 requires detail::CompatibleMatrixProductTensors<Output, A, B>
269 std::optional<Precision> p = {})
270{
271 assign_product(select_backend_for<Output, A, B>(assign_product_op{}), output, a, b, tensor_element_t<Output>{1}, p);
272}
274template <KernelBackendSelector Selector, uni20::AsyncTensorOutput Output, RankedTensorView<2> A, RankedTensorView<2> B>
275 requires detail::CompatibleMatrixProductTensors<Output, A, B>
276void add_product(Selector selector, async::Async<Output>& output, async::Async<A> const& a, async::Async<B> const& b,
277 std::optional<Precision> p = {})
278{
279 add_product(std::move(selector), output, a, b, tensor_element_t<Output>{1}, p);
280}
281
283template <uni20::AsyncTensorOutput Output, RankedTensorView<2> A, RankedTensorView<2> B>
284 requires detail::CompatibleMatrixProductTensors<Output, A, B>
286 std::optional<Precision> p = {})
287{
288 add_product(select_backend_for<Output, A, B>(gemm_op{}), output, a, b, tensor_element_t<Output>{1}, p);
289}
290
291} // namespace uni20::linalg
The Async<T> container: coroutine‐safe asynchronous read/write.
Coroutine-aware kernel dispatch with an ordinary blocking fallback.
Utilities for awaiting on Async<T>: co_await overload, all(), try_await().
Canonical host-oriented coroutine task type.
Definition async_task.hpp:196
Async<T> is a container for coroutine-safe asynchronous read/write.
Definition async/async.hpp:93
ReadBuffer< T > read() const
Begin an asynchronous read of the value.
Definition async/async.hpp:339
EpochQueue const & queue() const
Inspect the shared epoch queue.
Definition async/async.hpp:423
WriteBuffer< T > write()
Begin exclusive asynchronous access to the payload.
Definition async/async.hpp:346
RAII handle for reading the value from an Async container at a given epoch.
Definition buffers.hpp:53
Exclusive mutable-access buffer for one Async<T> epoch.
Definition buffers.hpp:859
Mutable Tensor output supported by async operation wrappers.
Definition linalg/async/concepts.hpp:25
Coroutine scheduler for AsyncTask.
Coroutine kernel-dispatch customization for CublasBackend GEMM.
Capabilities shared by asynchronous Tensor operation wrappers.
Shared output capabilities for Async Tensor operation wrappers.
void schedule(AsyncTask &&task)
Schedule a task on the currently configured global scheduler.
Definition debug_scheduler.hpp:237
auto all(Aw &&... aw) noexcept
Build an awaitable that waits for all of the provided awaitables.
Definition awaiters.hpp:200
ReadBuffer< T > read(Async< T > const &a)
Convenience helper that forwards to Async<T>::read().
Definition async/async.hpp:540
Definition linalg/async.hpp:28
void gemm(BackendSelector selector, async::Async< OutputTensor > &output, Alpha &&alpha, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Beta &&beta, std::optional< Precision > p={})
Schedule fixed-output GEMM with an explicit backend selector.
Definition async/matrix_product.hpp:162
void assign_product(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Alpha &&alpha, std::optional< Precision > p={})
Schedule output = alpha * lhs * rhs with an explicit backend selector.
Definition async/matrix_product.hpp:198
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
void add_product(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Alpha &&alpha, std::optional< Precision > p={})
Schedule output += alpha * lhs * rhs with an explicit backend selector.
Definition async/matrix_product.hpp:234
Definition data_table.cpp:8
Output & prepare_output(Output &output, RequiredExtents const &required)
Prepare a mutable tensor output with the specified shape.
Definition tensor/output.hpp:104
void require_output(Output const &output, RequiredExtents const &required)
Require an existing tensor output to have the specified shape.
Definition tensor/output.hpp:84
constexpr Precision common_default_precision(A const &a, B const &b)
Select a binary operation's default without inspecting element precisions.
Definition tensor/precision.hpp:43
constexpr decltype(auto) mdspec_of(Tensor &tensor) noexcept(noexcept(tensor.mdspec()))
Return a tensor's immediate or descriptor-backed multidimensional metadata.
Definition tensor/concepts.hpp:50
std::remove_cv_t< typename tensor_mdspec_t< T >::element_type > tensor_element_t
Scalar value type exposed by a tensor-level object.
Definition tensor/concepts.hpp:126
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
Tensor-level matrix product update and overwrite operations.
Shape preparation contracts for tensor operation outputs.
#define ERROR_IF(cond,...)
Definition trace.hpp:389