Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
reverse_value.hpp
Go to the documentation of this file.
1#pragma once
4#include "async.hpp"
5#include "buffers.hpp"
6#include "epoch_queue.hpp"
7#include <type_traits>
8
9namespace uni20::async
10{
11
18template <typename T, typename U> AsyncTask async_accumulate(ReadBuffer<T> a_, ReadBuffer<U> b_, WriteBuffer<T> out_)
19{
20 auto a = co_await a_.transfer().maybe();
21 if (a)
22 {
23 T value = std::move(*a).get_release();
24 auto b = co_await b_.transfer().maybe();
25 if (b) value += std::move(*b).get_release();
26 co_await out_ = std::move(value);
27 }
28 else
29 {
30 // GCC 13 workaround for `(co_await ...).get()`: stage through a named proxy.
31 // GCC 14+ supports the one-liner form:
32 // U value = (co_await b_.transfer().or_cancel()).get();
33 auto b = co_await b_.transfer().or_cancel();
34 U value = b.get();
35 b.release();
36 co_await out_ += std::move(value);
37 }
38 co_return;
39}
40
47template <typename T, typename U>
49{
50 auto a = co_await a_.transfer().maybe();
51 if (a)
52 {
53 T value = std::move(*a).get_release();
54 auto b = co_await b_.transfer().maybe();
55 if (b) value -= std::move(*b).get_release();
56 co_await out_ = std::move(value);
57 }
58 else
59 {
60 auto b = co_await b_.transfer().or_cancel();
61 U value = b.get();
62 b.release();
63 co_await out_ -= std::move(value);
64 }
65 co_return;
66}
67
70template <typename T> class ReverseValue {
71 public:
72 using value_type = T;
73
75 ReverseValue() : async_(async_do_not_start), rqueue_(async_.queue().latest()) {}
76
78 ReverseValue(ReverseValue&&) noexcept = default;
79
83 ReverseValue& operator=(ReverseValue&& other) noexcept
84 {
85 if (this != &other)
86 {
87 this->finalize();
88 async_ = std::move(other.async_);
89 rqueue_ = std::move(other.rqueue_);
90 started_ = std::exchange(other.started_, false);
91 }
92 return *this;
93 }
94
96 ReverseValue(ReverseValue const&) = delete;
97
99 // Async<T> const& async() const noexcept { return async_; }
100
102 // ReadBuffer<T> final()
103 // {
104 // this->finalize();
105 // return async_.read();
106 // }
107
109 ~ReverseValue() { this->finalize(); }
110
113 Async<T> final()
114 {
115 this->finalize();
116 return async_;
117 }
118
121 [[nodiscard]] T final_wait()
122 {
123 this->finalize();
124 return async_.get_wait();
125 }
126
128 void wait()
129 {
130 this->finalize();
131 async_.wait();
132 }
133
136 [[nodiscard]] T get_wait() { return this->final_wait(); }
137
140 ReadBuffer<T> input() const { return this->read_buffer(); }
141
144 ReadBuffer<T> read() const { return this->read_buffer(); }
145
148 [[nodiscard]] WriteBuffer<T> output() { return this->write_buffer(); }
149
151 void finalize() const
152 {
153 if (!started_)
154 {
155 if (!rqueue_.is_started())
156 {
157 rqueue_.start();
158 }
159 started_ = true;
160 }
161 }
162
167 template <typename U>
169 requires std::constructible_from<T, U&&>
170 {
171 WriteBuffer<T> w(this->write_buffer());
172 rqueue_.start();
173 w.emplace_assert(std::forward<U>(v));
174 return *this;
175 }
176
181 template <typename U> ReverseValue& operator=(Async<U> const& v)
182 {
183 async_assign(this->write_buffer(), v.read());
184 rqueue_.start();
185 return *this;
186 }
187
192 template <typename U> ReverseValue& operator=(Async<U>&& v)
193 {
194 async_move(this->write_buffer(), std::move(v));
195 rqueue_.start();
196 return *this;
197 }
198
203 template <typename U> ReverseValue& operator+=(Async<U> const& v)
204 {
205 // It is important that we construct the buffer objects in the right order. Writer first,
206 // then reader, so the reader is the earlier epoch in the ReverseEpochQueue
207 WriteBuffer<T> w(this->write_buffer());
208 schedule(async_accumulate(this->read_buffer(), v.read(), std::move(w)));
209 return *this;
210 }
211
216 template <typename U> ReverseValue& operator+=(ReverseValue<U> const& v)
217 {
218 WriteBuffer<T> w(this->write_buffer());
219 schedule(async_accumulate(this->read_buffer(), v.read(), std::move(w)));
220 return *this;
221 }
222
227 template <typename U> ReverseValue& operator+=(ReadBuffer<U> v)
228 {
229 WriteBuffer<T> w(this->write_buffer());
230 schedule(async_accumulate(this->read_buffer(), std::move(v), std::move(w)));
231 return *this;
232 }
233
238 template <typename U> ReverseValue& operator-=(Async<U> const& v)
239 {
240 WriteBuffer<T> w(this->write_buffer());
241 schedule(async_accumulate_minus(this->read_buffer(), v.read(), std::move(w)));
242 return *this;
243 }
244
249 template <typename U> ReverseValue& operator-=(ReverseValue<U> const& v)
250 {
251 WriteBuffer<T> w(this->write_buffer());
252 schedule(async_accumulate_minus(this->read_buffer(), v.read(), std::move(w)));
253 return *this;
254 }
255
260 template <typename U> ReverseValue& operator-=(ReadBuffer<U> v)
261 {
262 WriteBuffer<T> w(this->write_buffer());
263 schedule(async_accumulate_minus(this->read_buffer(), std::move(v), std::move(w)));
264 return *this;
265 }
266
270 {
271 this->finalize();
272 return async_;
273 }
276 Async<T> const& backprop() const&
277 {
278 this->finalize();
279 return async_;
280 }
284 {
285 this->finalize();
286 return std::move(async_);
287 }
288
291 Async<T>& last_value() & { return async_; }
294 Async<T> const& last_value() const& { return async_; }
297 Async<T> last_value() && { return std::move(async_); }
298
300 void start() { rqueue_.start(); }
301
302 private:
305 ReadBuffer<T> read_buffer() const { return ReadBuffer<T>(rqueue_.create_read_context(async_.storage())); }
308 WriteBuffer<T> write_buffer() { return WriteBuffer<T>(rqueue_.create_write_context(async_.storage())); }
309
310 Async<T> async_;
311 mutable ReverseEpochQueue rqueue_; // must be mutable if we want read access to be logically const
312 mutable bool started_{false};
313};
314
315template <typename T> struct async_value_type<ReverseValue<T>>
316{
317 using type = T;
318};
319
324template <typename T> ReadBuffer<T> read(ReverseValue<T> const& x) { return x.input(); }
325
326} // namespace uni20::async
Awaitable capabilities for shared reads and exclusive mutable access to Async<T>.
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
RAII handle for reading the value from an Async container at a given epoch.
Definition buffers.hpp:53
ReadMaybeAwaiter< T const & > maybe() &
Returns a ReadMaybeAwaiter.
Definition buffers.hpp:521
ReadBuffer && transfer() &noexcept
Transfer this read buffer into the owning-await path.
Definition buffers.hpp:147
ReadOrCancelAwaiter< T const & > or_cancel() &
Returns a ReadOrCancelAwaiter.
Definition buffers.hpp:531
void start()
Start running the reverse queue.
Definition epoch_queue.hpp:151
EpochContextReader< T > create_read_context(shared_storage< T > storage)
Create a read context from the earliest active reverse epoch.
Definition epoch_queue.hpp:128
EpochContextWriter< T > create_write_context(shared_storage< T > storage)
Create a write context from the earliest active reverse epoch.
Definition epoch_queue.hpp:140
bool is_started() const
Reports whether the reverse queue has already started.
Definition epoch_queue.hpp:167
Reverse-mode accumulation endpoint with staged input/output gradient channels.
Definition reverse_value.hpp:70
void finalize() const
Ensure the reverse queue has been started exactly once.
Definition reverse_value.hpp:151
ReverseValue(ReverseValue &&) noexcept=default
Move constructor.
ReverseValue(ReverseValue const &)=delete
Non-copyable.
ReverseValue & operator+=(ReadBuffer< U > v)
Accumulate a read buffer source into this channel.
Definition reverse_value.hpp:227
ReverseValue & operator=(Async< U > &&v)
Move-assign from an async source by scheduling an async move.
Definition reverse_value.hpp:192
ReverseValue & operator-=(Async< U > const &v)
Subtract an async source from this reverse channel.
Definition reverse_value.hpp:238
ReverseValue & operator+=(Async< U > const &v)
Accumulate an async source into this reverse channel.
Definition reverse_value.hpp:203
T final_wait()
Finalize and synchronously wait for the terminal gradient value.
Definition reverse_value.hpp:121
T value_type
Definition reverse_value.hpp:72
void wait()
Finalize and synchronously wait for the terminal gradient to complete.
Definition reverse_value.hpp:128
ReverseValue & operator=(Async< U > const &v)
Assign from an async source by scheduling an async copy.
Definition reverse_value.hpp:181
void start()
Explicitly start the reverse queue.
Definition reverse_value.hpp:300
ReverseValue & operator+=(ReverseValue< U > const &v)
Accumulate another reverse channel into this channel.
Definition reverse_value.hpp:216
ReverseValue & operator-=(ReverseValue< U > const &v)
Subtract another reverse channel from this channel.
Definition reverse_value.hpp:249
Async< T > last_value() &&
Move out the latest internal async value without forcing finalization.
Definition reverse_value.hpp:297
WriteBuffer< T > output()
Get the output-gradient write buffer for downstream accumulation.
Definition reverse_value.hpp:148
Async< T > backprop() &&
Finalize and move out the backprop async channel.
Definition reverse_value.hpp:283
ReverseValue & operator-=(ReadBuffer< U > v)
Subtract a read-buffer source from this channel.
Definition reverse_value.hpp:260
T get_wait()
Wait for and return the final gradient value.
Definition reverse_value.hpp:136
~ReverseValue()
Access as an Async<T> (read-only use).
Definition reverse_value.hpp:109
ReadBuffer< T > input() const
Get the input-gradient read buffer from the earliest reverse epoch.
Definition reverse_value.hpp:140
Async< T > const & last_value() const &
Access the latest internal async value without forcing finalization.
Definition reverse_value.hpp:294
Async< T > & backprop() &
Finalize and return mutable access to the backprop async channel.
Definition reverse_value.hpp:269
ReverseValue()
Construct a new, uninitialized ReverseValue.
Definition reverse_value.hpp:75
Async< T > const & backprop() const &
Finalize and return const access to the backprop async channel.
Definition reverse_value.hpp:276
Async< T > & last_value() &
Access the latest internal async value without forcing finalization.
Definition reverse_value.hpp:291
ReverseValue & operator=(U &&v)
Immediately assign a terminal gradient value.
Definition reverse_value.hpp:168
ReadBuffer< T > read() const
Alias for input().
Definition reverse_value.hpp:144
Exclusive mutable-access buffer for one Async<T> epoch.
Definition buffers.hpp:859
T & emplace_assert(Args &&... args)
Construct the destination value in-place when immediately writable.
Definition buffers.hpp:985
locale_t value
Definition data_table.cpp:49
FIFO queue enforcing writer→readers→next-writer ordering.
void async_move(WriteBuffer< T > lhs, U &&rhs)
Definition async_ops.hpp:369
void async_assign(WriteBuffer< T > destination, U &&source)
Assign an awaitable value into an existing independent-value timeline.
Definition async_ops.hpp:310
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
void schedule(AsyncTask &&task)
Schedule a task on the currently configured global scheduler.
Definition debug_scheduler.hpp:237
AsyncTask async_accumulate(ReadBuffer< T > a_, ReadBuffer< U > b_, WriteBuffer< T > out_)
Compute out = a + b while tolerating cancellation of either input.
Definition reverse_value.hpp:18
ReadBuffer< T > read(Async< T > const &a)
Convenience helper that forwards to Async<T>::read().
Definition async/async.hpp:540
AsyncTask async_accumulate_minus(ReadBuffer< T > a_, ReadBuffer< U > b_, WriteBuffer< T > out_)
Compute out = a - b while tolerating cancellation of either input.
Definition reverse_value.hpp:48
constexpr async_do_not_start_t async_do_not_start
Tag constant to construct an Async without starting the queue object.
Definition async/async.hpp:35
T type
Definition reverse_value.hpp:317
Strip Async<T> to T for assignment and expression type deduction.
Definition async_traits.hpp:20
Async aliases for lazy tensor-level views.