Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
linalg/async/transform.hpp
Go to the documentation of this file.
1#pragma once
2
16
17#include <concepts>
18#include <cstddef>
19#include <tuple>
20#include <utility>
21
22namespace uni20
23{
24namespace detail
25{
26
27template <class OutputTensor, class... InputTensors>
28void validate_async_transform_aliasing(async::Async<OutputTensor> const& output,
29 async::Async<InputTensors> const&... inputs)
30{
31 if constexpr (sizeof...(InputTensors) > 0)
32 {
33 auto const* const output_queue = std::addressof(output.queue());
34 auto validate_input = [&](auto const& input) {
35 ERROR_IF(output_queue == std::addressof(input.queue()),
36 "async elementwise transform output must not share an epoch queue with an input");
37 };
38 (validate_input(inputs), ...);
39 }
40}
41
42template <AsyncTensorOutput OutputTensor, TensorView FirstInputTensor>
43[[nodiscard]] OutputTensor& prepare_async_assign_transform_output(async::shared_storage<OutputTensor>& storage,
44 FirstInputTensor const& first_input)
45{
46 if (storage.constructed()) return *storage;
47
48 using extents_type = tensor_extents_t<OutputTensor>;
49 if constexpr (std::constructible_from<OutputTensor, uninitialized_t, extents_type const&>)
50 {
51 auto const extents = convert_tensor_extents<extents_type>(first_input.extents());
52 return storage.emplace(uninitialized, extents);
53 }
54 else if constexpr (std::constructible_from<OutputTensor, extents_type const&>)
55 {
56 auto const extents = convert_tensor_extents<extents_type>(first_input.extents());
57 return storage.emplace(extents);
58 }
59 else
60 {
62 }
63}
64
65template <class BackendSelector, class Function, AsyncTensorOutput OutputTensor, class Awaited, std::size_t... Input>
66void invoke_async_assign_transform(BackendSelector const& selector, linalg::transform_op<Function>& operation,
67 OutputTensor& output, Awaited& awaited, std::index_sequence<Input...>)
68{
69 uni20::assign_transform(selector, output, std::move(operation.function), std::get<Input + 1>(awaited)...);
70}
71
72template <class BackendSelector, class Function, AsyncTensorOutput OutputTensor, class Awaited, std::size_t... Input>
73void invoke_async_transform_inplace(BackendSelector const& selector, linalg::transform_inplace_op<Function>& operation,
74 OutputTensor& output, Awaited& awaited, std::index_sequence<Input...>)
75{
76 uni20::transform_inplace(selector, output, std::move(operation.function), std::get<Input + 1>(awaited)...);
77}
78
79template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
80async::AsyncTask co_assign_transform(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
81 linalg::transform_op<Function> operation,
83{
84 if constexpr (async::is_async_alias_v<OutputTensor>)
85 {
86 AsyncAliasWriteDescriptorAwaiter output_descriptor(output);
87 auto awaited = co_await async::all(output_descriptor, inputs...);
88 auto mutable_output = std::get<0>(awaited);
89 require_output(mutable_output, std::get<1>(awaited).extents());
90 invoke_async_assign_transform(selector, operation, mutable_output, awaited,
91 std::index_sequence_for<InputTensors...>{});
92 }
93 else
94 {
95 auto output_storage = output.storage();
96 auto awaited = co_await async::all(output_storage, inputs...);
97 auto& storage = std::get<0>(awaited);
98 auto& output_value = prepare_async_assign_transform_output(storage, std::get<1>(awaited));
99 invoke_async_assign_transform(selector, operation, output_value, awaited,
100 std::index_sequence_for<InputTensors...>{});
101 }
102 co_return;
103}
104
105template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
106async::AsyncTask co_transform_inplace(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
107 linalg::transform_inplace_op<Function> operation,
109{
110 if constexpr (async::is_async_alias_v<OutputTensor>)
111 {
112 AsyncAliasWriteDescriptorAwaiter output_descriptor(output);
113 auto awaited = co_await async::all(output_descriptor, inputs...);
114 auto mutable_output = std::get<0>(awaited);
115 invoke_async_transform_inplace(selector, operation, mutable_output, awaited,
116 std::index_sequence_for<InputTensors...>{});
117 }
118 else
119 {
120 auto output_storage = output.storage();
121 auto awaited = co_await async::all(output_storage, inputs...);
122 auto& storage = std::get<0>(awaited);
123 if (!storage.constructed()) throw async::buffer_write_uninitialized{};
124 invoke_async_transform_inplace(selector, operation, *storage, awaited, std::index_sequence_for<InputTensors...>{});
125 }
126 co_return;
127}
128
129template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function>
130async::AsyncTask co_assign_transform_existing(BackendSelector const selector, async::WriteBuffer<OutputTensor> output,
131 linalg::transform_op<Function> operation)
132{
133 if constexpr (async::is_async_alias_v<OutputTensor>)
134 {
135 AsyncAliasWriteDescriptorAwaiter output_descriptor(output);
136 auto awaited = co_await async::all(output_descriptor);
137 auto mutable_output = std::get<0>(awaited);
138 uni20::assign_transform(selector, mutable_output, std::move(operation.function));
139 }
140 else
141 {
142 auto output_storage = output.storage();
143 auto awaited = co_await async::all(output_storage);
144 auto& storage = std::get<0>(awaited);
145 if (!storage.constructed()) throw async::buffer_write_uninitialized{};
146 uni20::assign_transform(selector, *storage, std::move(operation.function));
147 }
148 co_return;
149}
150
151template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
152void schedule_async_assign_transform(BackendSelector selector, async::Async<OutputTensor>& output,
153 linalg::transform_op<Function> operation,
154 async::Async<InputTensors> const&... inputs)
155{
156 validate_async_transform_aliasing(output, inputs...);
157 auto task = co_assign_transform(std::move(selector), output.write(), std::move(operation), inputs.read()...);
158 task.debug_name("assign_transform");
159 async::schedule(std::move(task));
160}
161
162template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
163void schedule_async_transform_inplace(BackendSelector selector, async::Async<OutputTensor>& output,
164 linalg::transform_inplace_op<Function> operation,
165 async::Async<InputTensors> const&... inputs)
166{
167 validate_async_transform_aliasing(output, inputs...);
168 auto task = co_transform_inplace(std::move(selector), output.write(), std::move(operation), inputs.read()...);
169 task.debug_name("transform_inplace");
170 async::schedule(std::move(task));
171}
172
173template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function>
174void schedule_async_assign_transform_existing(BackendSelector selector, async::Async<OutputTensor>& output,
175 linalg::transform_op<Function> operation)
176{
177 auto task = co_assign_transform_existing(std::move(selector), output.write(), std::move(operation));
178 task.debug_name("assign_transform");
179 async::schedule(std::move(task));
180}
181
182} // namespace detail
183
187template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function>
188void assign_transform(BackendSelector selector, async::Async<OutputTensor>& output, Function&& function)
189{
190 auto operation = linalg::transform_op{std::forward<Function>(function)};
191 detail::schedule_async_assign_transform_existing(std::move(selector), output, std::move(operation));
192}
193
195template <AsyncTensorOutput OutputTensor, class Function>
196void assign_transform(async::Async<OutputTensor>& output, Function&& function)
197{
198 auto operation = linalg::transform_op{std::forward<Function>(function)};
199 auto selector = linalg::select_backend_for<OutputTensor>(operation);
200 detail::schedule_async_assign_transform_existing(std::move(selector), output, std::move(operation));
201}
202
204template <class BackendSelector, AsyncTensorOutput OutputTensor, class Value>
205void fill(BackendSelector selector, async::Async<OutputTensor>& output, Value&& value)
206{
207 assign_transform(std::move(selector), output, linalg::constant{std::forward<Value>(value)});
208}
209
211template <AsyncTensorOutput OutputTensor, class Value> void fill(async::Async<OutputTensor>& output, Value&& value)
212{
213 assign_transform(output, linalg::constant{std::forward<Value>(value)});
214}
215
222template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView FirstInputTensor,
223 TensorView... RestInputTensors>
224 requires detail::OverwriteTransformTensors<OutputTensor, FirstInputTensor, RestInputTensors...>
225void assign_transform(BackendSelector selector, async::Async<OutputTensor>& output, Function&& function,
226 async::Async<FirstInputTensor> const& first_input,
227 async::Async<RestInputTensors> const&... rest_inputs)
228{
229 auto operation = linalg::transform_op{std::forward<Function>(function)};
230 detail::schedule_async_assign_transform(std::move(selector), output, std::move(operation), first_input,
231 rest_inputs...);
232}
233
239template <AsyncTensorOutput OutputTensor, class Function, TensorView FirstInputTensor, TensorView... RestInputTensors>
240 requires detail::OverwriteTransformTensors<OutputTensor, FirstInputTensor, RestInputTensors...>
241void assign_transform(async::Async<OutputTensor>& output, Function&& function,
242 async::Async<FirstInputTensor> const& first_input,
243 async::Async<RestInputTensors> const&... rest_inputs)
244{
245 auto operation = linalg::transform_op{std::forward<Function>(function)};
246 auto selector = linalg::select_backend_for<OutputTensor, FirstInputTensor, RestInputTensors...>(operation);
247 detail::schedule_async_assign_transform(std::move(selector), output, std::move(operation), first_input,
248 rest_inputs...);
249}
250
256template <class BackendSelector, AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
257 requires detail::UpdateTransformTensors<OutputTensor, InputTensors...>
258void transform_inplace(BackendSelector selector, async::Async<OutputTensor>& output, Function&& function,
259 async::Async<InputTensors> const&... inputs)
260{
261 auto operation = linalg::transform_inplace_op{std::forward<Function>(function)};
262 detail::schedule_async_transform_inplace(std::move(selector), output, std::move(operation), inputs...);
263}
264
269template <AsyncTensorOutput OutputTensor, class Function, TensorView... InputTensors>
270 requires detail::UpdateTransformTensors<OutputTensor, InputTensors...>
271void transform_inplace(async::Async<OutputTensor>& output, Function&& function,
272 async::Async<InputTensors> const&... inputs)
273{
274 auto operation = linalg::transform_inplace_op{std::forward<Function>(function)};
275 auto selector = linalg::select_backend_for<OutputTensor, InputTensors...>(operation);
276 detail::schedule_async_transform_inplace(std::move(selector), output, std::move(operation), inputs...);
277}
278
279} // namespace uni20
The Async<T> container: coroutine‐safe asynchronous read/write.
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
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
locale_t value
Definition data_table.cpp:49
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
constexpr auto select_backend_for(Operation const &operation)
Resolve the immutable backend selector for Tensor operand types.
Definition backend_selector.hpp:201
Definition async/async.hpp:26
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
void require_output(Output const &output, RequiredExtents const &required)
Require an existing tensor output to have the specified shape.
Definition tensor/output.hpp:84
void fill(BackendSelector selector, async::Async< OutputTensor > &output, Value &&value)
Fill an existing async Tensor without reading its old values.
Definition linalg/async/transform.hpp:205
void transform_inplace(BackendSelector selector, async::Async< OutputTensor > &output, Function &&function, async::Async< InputTensors > const &... inputs)
Schedule a variadic elementwise update with an explicit backend selector.
Definition linalg/async/transform.hpp:258
void assign_transform(BackendSelector selector, async::Async< OutputTensor > &output, Function &&function)
Schedule a nullary overwrite of an existing async Tensor.
Definition linalg/async/transform.hpp:188
Nullary elementwise function that returns one retained value.
Definition elementwise_functions.hpp:20
Callable-carrying elementwise update operation.
Definition operation_tags.hpp:61
Callable-carrying elementwise overwrite operation.
Definition operation_tags.hpp:48
Shape preparation contracts for tensor operation outputs.
Backend-dispatched elementwise overwrite and update operations.
#define ERROR_IF(cond,...)
Definition trace.hpp:389