Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
linalg/async/copy.hpp
Go to the documentation of this file.
1#pragma once
2
15#include <uni20/tensor/copy.hpp>
16
17#if UNI20_BACKEND_CUDA
20#endif
21
22#include <concepts>
23#include <memory>
24#include <tuple>
25#include <type_traits>
26#include <utility>
27
28namespace uni20
29{
30namespace detail
31{
32
33template <class OutputTensor, class InputTensor>
34concept AsyncCopyTensors = AsyncTensorOutput<OutputTensor> && TensorView<InputTensor> &&
35 (tensor_mdspec_t<OutputTensor>::rank() == tensor_mdspec_t<InputTensor>::rank()) &&
36 std::same_as<tensor_element_t<OutputTensor>, tensor_element_t<InputTensor>>;
37
38#if UNI20_BACKEND_CUDA
39template <cuda::BufferMdspec InputMdspan>
40[[nodiscard]] cuda::DeviceResources& cuda_copy_resources(InputMdspan const& input)
41{
42 return input.data_descriptor().buffer().resources();
43}
44#endif
45
46template <AsyncTensorOutput OutputTensor, TensorView InputTensor>
47[[nodiscard]] OutputTensor& prepare_async_copy_output(async::shared_storage<OutputTensor>& storage,
48 InputTensor const& input)
49{
50 if (storage.constructed())
51 {
52 prepare_output(*storage, input.extents());
53 return *storage;
54 }
55
56 using extents_type = tensor_extents_t<OutputTensor>;
57 auto const extents = convert_tensor_extents<extents_type>(input.extents());
58 if constexpr (std::constructible_from<OutputTensor, extents_type const&>)
59 {
60 if constexpr (std::constructible_from<OutputTensor, uninitialized_t, extents_type const&>)
61 return storage.emplace(uninitialized, extents);
62 else
63 return storage.emplace(extents);
64 }
65#if UNI20_BACKEND_CUDA
66 else if constexpr (std::same_as<tensor_storage_policy_t<OutputTensor>, CudaStorage> &&
67 cuda::BufferMdspec<tensor_mdspec_t<InputTensor>>)
68 {
69 auto input_descriptor = mdspec_of(input);
70 return storage.emplace(uninitialized, cuda_copy_resources(input_descriptor), extents);
71 }
72#endif
73 else
74 {
76 }
77}
78
79template <class BackendSelector, AsyncTensorOutput OutputTensor, TensorView InputTensor>
80async::AsyncTask co_copy(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
82{
83 if constexpr (async::is_async_alias_v<OutputTensor>)
84 {
85 AsyncAliasWriteDescriptorAwaiter output_descriptor_awaiter(output);
86 auto awaited = co_await async::all(output_descriptor_awaiter, input);
87 auto output_value = std::get<0>(awaited);
88 auto const& input_value = std::get<1>(awaited);
89 prepare_output(output_value, input_value.extents());
90 auto output_descriptor = mdspec_of(output_value);
91 auto input_descriptor = mdspec_of(input_value);
92 co_await linalg::co_dispatch_kernel(selector, linalg::copy_op{}, output_descriptor, input_descriptor);
93 }
94 else
95 {
96 auto output_storage = output.storage();
97 auto awaited = co_await async::all(output_storage, input);
98 auto& storage = std::get<0>(awaited);
99 auto const& input_value = std::get<1>(awaited);
100 auto& output_value = prepare_async_copy_output(storage, input_value);
101 auto output_descriptor = mdspec_of(output_value);
102 auto input_descriptor = mdspec_of(input_value);
103 co_await linalg::co_dispatch_kernel(selector, linalg::copy_op{}, output_descriptor, input_descriptor);
104 }
105 co_return;
106}
107
108template <class BackendSelector, AsyncTensorOutput OutputTensor, TensorView InputTensor>
109void schedule_async_copy(BackendSelector selector, async::Async<OutputTensor>& output,
110 async::Async<InputTensor> const& input)
111{
112 ERROR_IF(std::addressof(output.queue()) == std::addressof(input.queue()),
113 "async copy output must not share an epoch queue with its input");
114 auto task = co_copy(std::move(selector), output.write(), input.read());
115 task.debug_name("copy");
116 async::schedule(std::move(task));
117}
118
119template <TensorView InputTensor>
120using async_materialized_tensor_t =
121 std::remove_cvref_t<decltype(uni20::make_tensor(std::declval<InputTensor const&>()))>;
122
123template <class BackendSelector, class ResultTensor, TensorView InputTensor>
124async::AsyncTask co_make_tensor(BackendSelector const selector, async::WriteBuffer<ResultTensor> output,
126{
127 auto output_storage = output.storage();
128 auto awaited = co_await async::all(output_storage, input);
129 auto& storage = std::get<0>(awaited);
130 auto const& input_value = std::get<1>(awaited);
131 ResultTensor result(uninitialized,
132 convert_tensor_extents<typename ResultTensor::extents_type>(input_value.extents()));
133 uni20::copy(selector, result, input_value);
134 storage.emplace(std::move(result));
135 co_return;
136}
137
138#if UNI20_BACKEND_CUDA
139template <TensorView InputTensor>
140using async_device_tensor_t = std::remove_cvref_t<decltype(uni20::to_device(std::declval<InputTensor const&>(),
141 std::declval<cuda::DeviceResources&>()))>;
142
143template <class ResultTensor, TensorView InputTensor>
145 cuda::DeviceResources* resources)
146{
147 auto output_storage = output.storage();
148 auto awaited = co_await async::all(output_storage, input);
149 auto& storage = std::get<0>(awaited);
150 auto const& input_value = std::get<1>(awaited);
151 ResultTensor result(uninitialized, *resources,
152 convert_tensor_extents<typename ResultTensor::extents_type>(input_value.extents()));
153 auto output_descriptor = mdspec_of(result);
154 auto input_descriptor = mdspec_of(input_value);
155 co_await linalg::co_dispatch_kernel(linalg::CudaReferenceBackend{}, linalg::copy_op{}, output_descriptor,
156 input_descriptor);
157 storage.emplace(std::move(result));
158 co_return;
159}
160#endif
161
162} // namespace detail
163
172template <class BackendSelector, class OutputTensor, class InputTensor>
173 requires detail::AsyncCopyTensors<OutputTensor, InputTensor>
174void copy(BackendSelector selector, async::Async<OutputTensor>& output, async::Async<InputTensor> const& input)
175{
176 detail::schedule_async_copy(std::move(selector), output, input);
177}
178
180template <class OutputTensor, class InputTensor>
181 requires detail::AsyncCopyTensors<OutputTensor, InputTensor>
183{
184#if UNI20_BACKEND_CUDA
185 if constexpr (detail::is_pageable_cuda_transfer<OutputTensor, InputTensor>)
186 {
187 detail::schedule_async_copy(linalg::CudaReferenceBackend{}, output, input);
188 }
189 else
190#endif
191 {
192 auto selector = linalg::select_backend_for<OutputTensor, InputTensor>(linalg::copy_op{});
193 detail::schedule_async_copy(std::move(selector), output, input);
194 }
195}
196
199template <TensorView InputTensor> [[nodiscard]] auto make_tensor(async::Async<InputTensor> const& input)
200{
201 using result_type = detail::async_materialized_tensor_t<InputTensor>;
203 output.debug_name("make_tensor.result");
204#if UNI20_BACKEND_CUDA
205 auto selector = [&] {
206 if constexpr (detail::is_pageable_cuda_transfer<result_type, InputTensor>)
208 else
209 return linalg::select_backend_for<result_type, InputTensor>(linalg::copy_op{});
210 }();
211#else
212 auto selector = linalg::select_backend_for<result_type, InputTensor>(linalg::copy_op{});
213#endif
214 auto task = detail::co_make_tensor(std::move(selector), output.write(), input.read());
215 task.debug_name("make_tensor");
216 async::schedule(std::move(task));
217 return output;
218}
219
220#if UNI20_BACKEND_CUDA
222template <TensorView InputTensor>
223 requires std::same_as<tensor_storage_policy_t<InputTensor>, CudaStorage>
224[[nodiscard]] auto to_host(async::Async<InputTensor> const& input)
225{
226 return uni20::make_tensor(input);
227}
228
232template <TensorView InputTensor>
233 requires detail::CanonicalReshapeLayout<typename tensor_mdspec_t<InputTensor>::layout_type>
234[[nodiscard]] auto to_device(async::Async<InputTensor> const& input, cuda::DeviceResources& resources)
235{
236 using result_type = detail::async_device_tensor_t<InputTensor>;
238 output.debug_name("to_device.result");
239 auto task = detail::co_to_device(output.write(), input.read(), std::addressof(resources));
240 task.debug_name("to_device");
241 async::schedule(std::move(task));
242 return output;
243}
244
246template <TensorView InputTensor>
247 requires detail::CanonicalReshapeLayout<typename tensor_mdspec_t<InputTensor>::layout_type>
248[[nodiscard]] auto to_device(async::Async<InputTensor> const& input, int device)
249{
250 return uni20::to_device(input, cuda::device_resources(device));
251}
252
254template <TensorView InputTensor>
255 requires detail::CanonicalReshapeLayout<typename tensor_mdspec_t<InputTensor>::layout_type>
256[[nodiscard]] auto to_device(async::Async<InputTensor> const& input, cuda::Device device)
257{
258 return uni20::to_device(input, device.ordinal());
259}
260#endif
261
262} // namespace uni20
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
Async & debug_name(std::string const &label)
Assign an optional debug label for task-registry graph output.
Definition async/async.hpp:212
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
auto storage() &
Awaiter yielding direct access to writer shared storage.
Definition buffers.hpp:1029
Raised when a writer requests a mutable reference before construction.
Definition async_errors.hpp:74
A lightweight, thread-safe, reference-counted storage for a single object.
Definition shared_storage.hpp:94
bool constructed() const noexcept
Reports whether a value is currently constructed.
Definition shared_storage.hpp:234
T & emplace(Args &&... args)
Destroy any existing value and construct a new one in place.
Definition shared_storage.hpp:270
Coroutine kernel-dispatch customization for CUDA Tensor copies.
CUDA Tensor storage with deferred device-memory data descriptors.
Coroutine scheduler for AsyncTask.
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
DeviceResources & device_resources()
Return canonical resources for the installed runtime's default device.
Definition runtime.cpp:820
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
Definition async/async.hpp:26
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
Output & prepare_output(Output &output, RequiredExtents const &required)
Prepare a mutable tensor output with the specified shape.
Definition tensor/output.hpp:104
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
auto make_tensor(async::Async< InputTensor > const &input)
Materialize an async Tensor view as an independent owning host Tensor.
Definition linalg/async/copy.hpp:199
void copy(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a Tensor copy through an explicit backend selector.
Definition linalg/async/copy.hpp:174
Backend value for generic CUDA runtime kernels and transfers.
Definition backend_selector.hpp:48
Accessor-respecting element copy operation tag.
Definition operation_tags.hpp:33
Backend-dispatched tensor copies and inferred owning materialization.
#define ERROR_IF(cond,...)
Definition trace.hpp:389