ttg 1.0.0
Template Task Graph (TTG): flowgraph-based programming model for high-performance distributed-memory algorithms
Loading...
Searching...
No Matches
task.h
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2#ifndef TTG_DEVICE_TASK_H
3#define TTG_DEVICE_TASK_H
4
5#include <array>
6#include <type_traits>
7#include <span>
8
9
10#include "ttg/fwd.h"
11#include "ttg/impl_selector.h"
12#include "ttg/ptr.h"
13#include "ttg/devicescope.h"
14
15#ifdef TTG_HAVE_COROUTINE
16
17namespace ttg::device {
18
19 namespace detail {
20
21 struct device_input_data_t {
22 using impl_data_t = decltype(TTG_IMPL_NS::buffer_data(std::declval<ttg::Buffer<int>>()));
23
24 device_input_data_t(impl_data_t data, ttg::scope scope, bool isconst, bool isscratch)
25 : impl_data(data), scope(scope), is_const(isconst), is_scratch(isscratch)
26 { }
27 impl_data_t impl_data;
29 bool is_const;
30 bool is_scratch;
31 };
32
33 template <typename... Ts>
34 struct to_device_t {
35 std::tuple<std::add_lvalue_reference_t<Ts>...> ties;
36 };
37
38 /* extract buffer information from to_device_t */
39 template<typename... Ts, std::size_t... Is>
40 auto extract_buffer_data(detail::to_device_t<Ts...>& a, std::index_sequence<Is...>) {
41 using arg_types = std::tuple<Ts...>;
42 return std::array<device_input_data_t, sizeof...(Ts)>{
43 device_input_data_t{TTG_IMPL_NS::buffer_data(std::get<Is>(a.ties)),
44 std::get<Is>(a.ties).scope(),
47 }
48 } // namespace detail
49
57 struct Input {
58 private:
59 std::vector<detail::device_input_data_t> m_data;
60
61 public:
62 Input() { }
63 template<typename... Args>
64 Input(Args&&... args)
65 : m_data{{TTG_IMPL_NS::buffer_data(args), args.scope(),
66 std::is_const_v<std::remove_reference_t<Args>>,
68 { }
69
70 template<typename T>
71 void add(T&& v) {
72 using type = std::remove_reference_t<T>;
73 m_data.emplace_back(TTG_IMPL_NS::buffer_data(v), v.scope(), std::is_const_v<type>,
75 }
76
77 ttg::span<detail::device_input_data_t> span() {
78 return ttg::span(m_data);
79 }
80 };
81
82 namespace detail {
83 // overload for Input
84 template <>
85 struct to_device_t<Input> {
86 Input& input;
87 };
88 } // namespace detail
89
97 template <typename... Args>
98 [[nodiscard]]
99 inline auto select(Args &&...args) {
100 return detail::to_device_t<std::remove_reference_t<Args>...>{std::tie(std::forward<Args>(args)...)};
101 }
102
103 [[nodiscard]]
104 inline auto select(Input& input) {
105 return detail::to_device_t<Input>{input};
106 }
107
108 namespace detail {
109
110 enum ttg_device_coro_state {
111 TTG_DEVICE_CORO_STATE_NONE,
112 TTG_DEVICE_CORO_INIT,
113 TTG_DEVICE_CORO_WAIT_TRANSFER,
114 TTG_DEVICE_CORO_WAIT_KERNEL,
115 TTG_DEVICE_CORO_SENDOUT,
116 TTG_DEVICE_CORO_COMPLETE
117 };
118
119 template <typename... Ts>
120 struct wait_kernel_t {
121 std::tuple<std::add_lvalue_reference_t<Ts>...> ties;
122
123 /* always suspend */
124 constexpr bool await_ready() const noexcept { return false; }
125
126 /* always suspend */
127 template <typename Promise>
128 constexpr void await_suspend(ttg::coroutine_handle<Promise>) const noexcept {}
129
130 void await_resume() noexcept {
131 if constexpr (sizeof...(Ts) > 0) {
132 /* hook to allow the backend to handle the data after pushout */
133 TTG_IMPL_NS::post_device_out(ties);
134 }
135 }
136 };
137 } // namespace detail
138
144 template <typename... Buffers>
145 [[nodiscard]]
146 inline auto wait(Buffers &&...args) {
147 static_assert(
149 ...),
150 "Only ttg::Buffer and ttg::devicescratch can be waited on!");
151 return detail::wait_kernel_t<std::remove_reference_t<Buffers>...>{std::tie(std::forward<Buffers>(args)...)};
152 }
153
154 /******************************
155 * Send/Broadcast handling
156 * We pass the value returned by the backend's copy handler into a coroutine
157 * and execute the first part (prepare), before suspending it.
158 * The second part (send/broadcast) is executed after the task completed.
159 ******************************/
160
161 namespace detail {
162 struct send_coro_promise_type;
163
164 using send_coro_handle_type = ttg::coroutine_handle<send_coro_promise_type>;
165
167 struct send_coro_state : public send_coro_handle_type {
168 using base_type = send_coro_handle_type;
169
172
173 using promise_type = send_coro_promise_type;
174
176
177 send_coro_state(base_type base) : base_type(std::move(base)) {}
178
179 base_type &handle() { return *this; }
180
182 inline bool ready() { return true; }
183
185 inline bool completed();
186 };
187
189 struct send_coro_promise_type {
190 /* do not suspend the coroutine on first invocation, we want to run
191 * the coroutine immediately and suspend only once.
192 */
193 ttg::suspend_never initial_suspend() { return {}; }
194
195 /* we don't suspend the coroutine at the end.
196 * it can be destroyed once the send/broadcast is done
197 */
198 ttg::suspend_never final_suspend() noexcept { return {}; }
199
200 send_coro_state get_return_object() { return send_coro_state{send_coro_handle_type::from_promise(*this)}; }
201
202 /* the send coros only have an empty co_await */
203 ttg::suspend_always await_transform(ttg::Void) { return {}; }
204
205 void unhandled_exception() {
206 std::cerr << "Send coroutine caught an unhandled exception!" << std::endl;
207 throw; // fwd
208 }
209
210 void return_void() {}
211 };
212
213 template <typename Key, typename Value, ttg::Runtime Runtime = ttg::ttg_runtime>
214 inline send_coro_state send_coro(const Key &key, Value &&value, ttg::Out<Key, std::decay_t<Value>> &t,
216 ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
217 Key k = key;
218 t.prepare_send(k, std::forward<Value>(value));
219 co_await ttg::Void{}; // we'll come back once the task is done
220 t.send(k, std::forward<Value>(value));
221 };
222
223 template <typename Value, ttg::Runtime Runtime = ttg::ttg_runtime>
224 inline send_coro_state sendv_coro(Value &&value, ttg::Out<void, std::decay_t<Value>> &t,
226 ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
227 t.prepare_send(std::forward<Value>(value));
228 co_await ttg::Void{}; // we'll come back once the task is done
229 t.sendv(std::forward<Value>(value));
230 };
231
232 template <typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
233 inline send_coro_state sendk_coro(const Key &key, ttg::Out<Key, void> &t) {
234 // no need to prepare the send but we have to suspend once
235 Key k = key;
236 co_await ttg::Void{}; // we'll come back once the task is done
237 t.sendk(k);
238 };
239
240 template <ttg::Runtime Runtime = ttg::ttg_runtime>
241 inline send_coro_state send_coro(ttg::Out<void, void> &t) {
242 // no need to prepare the send but we have to suspend once
243 co_await ttg::Void{}; // we'll come back once the task is done
244 t.send();
245 };
246
247 struct send_t {
248 send_coro_state coro;
249 };
250 } // namespace detail
251
252 template <size_t i, typename keyT, typename valueT, typename... out_keysT, typename... out_valuesT,
254 inline detail::send_t send(const keyT &key, valueT &&value, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
256 return detail::send_t{
257 detail::send_coro(key, copy_handler(std::forward<valueT>(value)), std::get<i>(t), copy_handler)};
258 }
259
260 template <size_t i, typename valueT, typename... out_keysT, typename... out_valuesT,
262 inline detail::send_t sendv(valueT &&value, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
264 return detail::send_t{detail::sendv_coro(copy_handler(std::forward<valueT>(value)), std::get<i>(t), copy_handler)};
265 }
266
267 template <size_t i, typename Key, typename... out_keysT, typename... out_valuesT,
269 inline detail::send_t sendk(const Key &key, std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
270 return detail::send_t{detail::sendk_coro(key, std::get<i>(t))};
271 }
272
273 // clang-format off
278 // clang-format on
279 template <typename keyT, typename valueT, ttg::Runtime Runtime = ttg::ttg_runtime>
280 inline detail::send_t send(size_t i, const keyT &key, valueT &&value) {
282 auto *terminal_ptr = ttg::detail::get_out_terminal<keyT, valueT>(i, "ttg::device::send(i, key, value)");
283 return detail::send_t{detail::send_coro(key, copy_handler(std::forward<valueT>(value)), *terminal_ptr, copy_handler)};
284 }
285
286 // clang-format off
292 // clang-format on
293 template <size_t i, typename keyT, typename valueT>
294 inline auto send(const keyT &key, valueT &&value) {
295 return ttg::device::send(i, key, std::forward<valueT>(value));
296 }
297
298
299 template <typename valueT, ttg::Runtime Runtime = ttg::ttg_runtime>
300 inline detail::send_t sendv(std::size_t i, valueT &&value) {
301 auto *terminal_ptr = ttg::detail::get_out_terminal<void, valueT>(i, "ttg::device::send(i, key, value)");
303 return detail::send_t{detail::sendv_coro(copy_handler(std::forward<valueT>(value)), *terminal_ptr, copy_handler)};
304 }
305
306 template <typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
307 inline detail::send_t sendk(std::size_t i, const Key& key) {
308 auto *terminal_ptr = ttg::detail::get_out_terminal<Key, void>(i, "ttg::device::send(i, key, value)");
309 return detail::send_t{detail::sendk_coro(key, *terminal_ptr)};
310 }
311
312 template <ttg::Runtime Runtime = ttg::ttg_runtime>
313 inline detail::send_t send(std::size_t i) {
314 auto *terminal_ptr = ttg::detail::get_out_terminal<void, void>(i, "ttg::device::send(i, key, value)");
315 return detail::send_t{detail::send_coro(*terminal_ptr)};
316 }
317
318
319 template <std::size_t i, typename valueT, ttg::Runtime Runtime = ttg::ttg_runtime>
320 inline detail::send_t sendv(valueT &&value) {
321 return sendv(i, std::forward<valueT>(value));
322 }
323
324 template <size_t i, typename Key, ttg::Runtime Runtime = ttg::ttg_runtime>
325 inline detail::send_t sendk(const Key& key) {
326 return sendk(i, key);
327 }
328
329 template <size_t i, ttg::Runtime Runtime = ttg::ttg_runtime>
330 inline detail::send_t sendk() {
331 return send(i);
332 }
333
334 namespace detail {
335
336 template<typename T, typename Enabler = void>
337 struct broadcast_keylist_trait {
338 using key_type = T;
339 };
340
341 /* overload for iterable types that extracts the type of the first element */
342 template<typename T>
343 struct broadcast_keylist_trait<T, std::enable_if_t<ttg::meta::is_iterable_v<T>>> {
344 using key_type = decltype(*std::begin(std::declval<T>()));
345 };
346
347 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
348 typename... out_keysT, typename... out_valuesT>
349 inline void prepare_broadcast(const std::tuple<RangesT...> &keylists, valueT &&value,
350 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
351 std::get<I>(t).prepare_send(std::get<KeyId>(keylists), std::forward<valueT>(value));
352 if constexpr (sizeof...(Is) > 0) {
353 prepare_broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value), t);
354 }
355 }
356
357 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT>
358 inline void prepare_broadcast(const std::tuple<RangesT...> &keylists, valueT &&value) {
359 using key_t = typename broadcast_keylist_trait<
360 std::tuple_element_t<KeyId, std::tuple<std::remove_reference_t<RangesT>...>>
361 >::key_type;
362 auto *terminal_ptr = ttg::detail::get_out_terminal<key_t, valueT>(I, "ttg::device::broadcast(keylists, value)");
363 terminal_ptr->prepare_send(std::get<KeyId>(keylists), value);
364 if constexpr (sizeof...(Is) > 0) {
365 prepare_broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value));
366 }
367 }
368
369 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT,
370 typename... out_keysT, typename... out_valuesT>
371 inline void broadcast(const std::tuple<RangesT...> &keylists, valueT &&value,
372 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
373 std::get<I>(t).broadcast(std::get<KeyId>(keylists), std::forward<valueT>(value));
374 if constexpr (sizeof...(Is) > 0) {
375 detail::broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value), t);
376 }
377 }
378
379 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT, typename valueT>
380 inline void broadcast(const std::tuple<RangesT...> &keylists, valueT &&value) {
381 using key_t = typename broadcast_keylist_trait<
382 std::tuple_element_t<KeyId, std::tuple<std::remove_reference_t<RangesT>...>>
383 >::key_type;
384 auto *terminal_ptr = ttg::detail::get_out_terminal<key_t, valueT>(I, "ttg::device::broadcast(keylists, value)");
385 terminal_ptr->broadcast(std::get<KeyId>(keylists), value);
386 if constexpr (sizeof...(Is) > 0) {
387 ttg::device::detail::broadcast<KeyId+1, Is...>(keylists, std::forward<valueT>(value));
388 }
389 }
390
391 /* overload with explicit terminals */
392 template <size_t I, size_t... Is, typename RangesT, typename valueT,
393 typename... out_keysT, typename... out_valuesT,
395 inline send_coro_state
396 broadcast_coro(RangesT &&keylists, valueT &&value,
397 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t,
399 ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
400 RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
402 // treat as tuple
403 static_assert(sizeof...(Is)+1 == std::tuple_size_v<std::remove_cvref_t<RangesT>>,
404 "Size of keylist tuple must match the number of output terminals");
405 prepare_broadcast<0, I, Is...>(kl, std::forward<valueT>(value), t);
406 co_await ttg::Void{}; // we'll come back once the task is done
407 ttg::device::detail::broadcast<0, I, Is...>(kl, std::forward<valueT>(value), t);
408 } else {
409 // create a tie to the captured keylist
410 prepare_broadcast<0, I, Is...>(std::tie(kl), std::forward<valueT>(value), t);
411 co_await ttg::Void{}; // we'll come back once the task is done
412 ttg::device::detail::broadcast<0, I, Is...>(std::tie(kl), std::forward<valueT>(value), t);
413 }
414 }
415
416 /* overload with implicit terminals */
417 template <size_t I, size_t... Is, typename RangesT, typename valueT,
419 inline send_coro_state
420 broadcast_coro(RangesT &&keylists, valueT &&value,
422 ttg::detail::value_copy_handler<Runtime> copy_handler = std::move(ch); // destroyed at the end of the coro
423 RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
425 // treat as tuple
426 static_assert(sizeof...(Is)+1 == std::tuple_size_v<std::remove_cvref_t<RangesT>>,
427 "Size of keylist tuple must match the number of output terminals");
428 prepare_broadcast<0, I, Is...>(kl, std::forward<valueT>(value));
429 co_await ttg::Void{}; // we'll come back once the task is done
430 ttg::device::detail::broadcast<0, I, Is...>(kl, std::forward<valueT>(value));
431 } else {
432 // create a tie to the captured keylist
433 prepare_broadcast<0, I, Is...>(std::tie(kl), std::forward<valueT>(value));
434 co_await ttg::Void{}; // we'll come back once the task is done
435 ttg::device::detail::broadcast<0, I, Is...>(std::tie(kl), std::forward<valueT>(value));
436 }
437 }
438
443 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT,
444 typename... out_keysT, typename... out_valuesT>
445 inline void broadcastk(const std::tuple<RangesT...> &keylists,
446 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
447 std::get<I>(t).broadcast(std::get<KeyId>(keylists));
448 if constexpr (sizeof...(Is) > 0) {
449 detail::broadcastk<KeyId+1, Is...>(keylists, t);
450 }
451 }
452
453 template <size_t KeyId, size_t I, size_t... Is, typename... RangesT>
454 inline void broadcastk(const std::tuple<RangesT...> &keylists) {
455 using key_t = typename broadcast_keylist_trait<
456 std::tuple_element_t<KeyId, std::tuple<std::remove_reference_t<RangesT>...>>
457 >::key_type;
458 auto *terminal_ptr = ttg::detail::get_out_terminal<key_t, void>(I, "ttg::device::broadcastk(keylists)");
459 terminal_ptr->broadcast(std::get<KeyId>(keylists));
460 if constexpr (sizeof...(Is) > 0) {
461 ttg::device::detail::broadcastk<KeyId+1, Is...>(keylists);
462 }
463 }
464
465 /* overload with explicit terminals */
466 template <size_t I, size_t... Is, typename RangesT,
467 typename... out_keysT, typename... out_valuesT,
469 inline send_coro_state
470 broadcastk_coro(RangesT &&keylists,
471 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
472 RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
474 // treat as tuple
475 static_assert(sizeof...(Is)+1 == std::tuple_size_v<std::remove_cvref_t<RangesT>>,
476 "Size of keylist tuple must match the number of output terminals");
477 co_await ttg::Void{}; // we'll come back once the task is done
478 ttg::device::detail::broadcastk<0, I, Is...>(kl, t);
479 } else {
480 // create a tie to the captured keylist
481 co_await ttg::Void{}; // we'll come back once the task is done
482 ttg::device::detail::broadcastk<0, I, Is...>(std::tie(kl), t);
483 }
484 }
485
486 /* overload with implicit terminals */
487 template <size_t I, size_t... Is, typename RangesT,
489 inline send_coro_state
490 broadcastk_coro(RangesT &&keylists) {
491 RangesT kl = std::forward<RangesT>(keylists); // capture the keylist(s)
493 // treat as tuple
494 static_assert(sizeof...(Is)+1 == std::tuple_size_v<std::remove_cvref_t<RangesT>>,
495 "Size of keylist tuple must match the number of output terminals");
496 co_await ttg::Void{}; // we'll come back once the task is done
497 ttg::device::detail::broadcastk<0, I, Is...>(kl);
498 } else {
499 // create a tie to the captured keylist
500 co_await ttg::Void{}; // we'll come back once the task is done
501 ttg::device::detail::broadcastk<0, I, Is...>(std::tie(kl));
502 }
503 }
504 } // namespace detail
505
506 /*
507 * The keylist(s) are forwarded into the coroutine frame: rvalues are moved into the frame,
508 * lvalues are captured by reference and must outlive the send. A tuple of keylists is used
509 * to broadcast to multiple terminals, one keylist per terminal.
510 */
511
512 /* overload with explicit terminals */
513 template <size_t I, size_t... Is, typename RangesT, typename valueT, typename... out_keysT, typename... out_valuesT,
515 [[nodiscard]]
516 inline detail::send_t broadcast(RangesT &&keylist,
517 valueT &&value,
518 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
520 return detail::send_t{
521 detail::broadcast_coro<I, Is...>(std::forward<RangesT>(keylist),
522 copy_handler(std::forward<valueT>(value)),
523 t, std::move(copy_handler))};
524 }
525
526 /* overload with implicit terminals */
527 template <size_t I, size_t... Is, typename RangesT, typename valueT,
529 [[nodiscard]]
530 inline detail::send_t broadcast(RangesT &&keylist, valueT &&value) {
532 return detail::send_t{
533 detail::broadcast_coro<I, Is...>(std::forward<RangesT>(keylist),
534 copy_handler(std::forward<valueT>(value)),
535 std::move(copy_handler))};
536 }
537
538 /* overload with explicit terminals */
539 template <size_t I, size_t... Is, typename RangesT, typename... out_keysT, typename... out_valuesT>
540 [[nodiscard]]
541 inline detail::send_t broadcastk(RangesT &&keylist,
542 std::tuple<ttg::Out<out_keysT, out_valuesT>...> &t) {
543 return detail::send_t{detail::broadcastk_coro<I, Is...>(std::forward<RangesT>(keylist), t)};
544 }
545
546 /* overload with implicit terminals */
547 template <size_t I, size_t... Is, typename RangesT>
548 [[nodiscard]]
549 inline detail::send_t broadcastk(RangesT &&keylist) {
550 return detail::send_t{detail::broadcastk_coro<I, Is...>(std::forward<RangesT>(keylist))};
551 }
552
553 template<typename... Args, ttg::Runtime Runtime = ttg::ttg_runtime>
554 [[nodiscard]]
555 std::vector<device::detail::send_t> forward(Args&&... args) {
556 // TODO: check the cost of this!
557 return std::vector<device::detail::send_t>{std::forward<Args>(args)...};
558 }
559
560 /*******************************************
561 * Device task promise and coroutine handle
562 *******************************************/
563
564 namespace detail {
565 // fwd-decl
566 struct device_task_promise_type;
567 // base type for ttg::device::Task
568 using device_task_handle_type = ttg::coroutine_handle<device_task_promise_type>;
569 } // namespace detail
570
572
579 struct Task : public detail::device_task_handle_type {
580 using base_type = detail::device_task_handle_type;
581
584
585 using promise_type = detail::device_task_promise_type;
586
588
589 Task(base_type base) : base_type(std::move(base)) {}
590
591 base_type& handle() { return *this; }
592
594 inline bool ready() {
595 return true;
596 }
597
599 inline bool completed();
600 };
601
602 namespace detail {
603
604 /* The promise type that stores the views provided by the
605 * application task coroutine on the first co_yield. It subsequently
606 * tracks the state of the task when it moves from waiting for transfers
607 * to waiting for the submitted kernel to complete. */
608 struct device_task_promise_type {
609
610 /* do not suspend the coroutine on first invocation, we want to run
611 * the coroutine immediately and suspend when we get the device transfers.
612 */
613 ttg::suspend_never initial_suspend() {
614 m_state = ttg::device::detail::TTG_DEVICE_CORO_INIT;
615 return {};
616 }
617
618 /* suspend the coroutine at the end of the execution
619 * so we can access the promise.
620 * TODO: necessary? maybe we can save one suspend here
621 */
622 ttg::suspend_always final_suspend() noexcept {
623 m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
624 return {};
625 }
626
627 /* Allow co_await on a tuple */
628 template<typename... Views>
629 ttg::suspend_always await_transform(std::tuple<Views&...> &views) {
630 return yield_value(views);
631 }
632
633 template<typename... Ts>
634 ttg::suspend_always await_transform(detail::to_device_t<Ts...>&& a) {
635 auto arr = detail::extract_buffer_data(a, std::make_index_sequence<sizeof...(Ts)>{});
636 bool need_transfer = !(TTG_IMPL_NS::register_device_memory(ttg::span(arr)));
637 /* TODO: are we allowed to not suspend here and launch the kernel directly? */
638 m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_TRANSFER;
639 return {};
640 }
641
642 ttg::suspend_always await_transform(detail::to_device_t<Input>&& a) {
643 bool need_transfer = !(TTG_IMPL_NS::register_device_memory(a.input.span()));
644 /* TODO: are we allowed to not suspend here and launch the kernel directly? */
645 m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_TRANSFER;
646 return {};
647 }
648
649 template<typename... Ts>
650 auto await_transform(detail::wait_kernel_t<Ts...>&& a) {
651 //std::cout << "yield_value: wait_kernel_t" << std::endl;
652 if constexpr (sizeof...(Ts) > 0) {
653 TTG_IMPL_NS::mark_device_out(a.ties);
654 }
655 m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_KERNEL;
656 return a;
657 }
658
659 ttg::suspend_always await_transform(std::vector<device::detail::send_t>&& v) {
660 m_sends = std::forward<std::vector<device::detail::send_t>>(v);
661 m_state = ttg::device::detail::TTG_DEVICE_CORO_SENDOUT;
662 return {};
663 }
664
665 ttg::suspend_always await_transform(device::detail::send_t&& v) {
666 m_sends.clear();
667 m_sends.push_back(std::forward<device::detail::send_t>(v));
668 m_state = ttg::device::detail::TTG_DEVICE_CORO_SENDOUT;
669 return {};
670 }
671
672 void return_void() {
673 m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
674 }
675
676 bool complete() const {
677 return m_state == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
678 }
679
680 ttg::device::Task get_return_object() { return {detail::device_task_handle_type::from_promise(*this)}; }
681
682 void unhandled_exception() {
683 std::cerr << "Task coroutine caught an unhandled exception!" << std::endl;
684 throw; // fwd
685 }
686
687 //using iterator = std::vector<device_obj_view>::iterator;
688
689 /* execute all pending send and broadcast operations */
690 void do_sends() {
691 for (auto& send : m_sends) {
692 send.coro();
693 }
694 m_sends.clear();
695 }
696
697 auto state() {
698 return m_state;
699 }
700
701 private:
702 std::vector<device::detail::send_t> m_sends;
703 ttg_device_coro_state m_state = ttg::device::detail::TTG_DEVICE_CORO_STATE_NONE;
704
705 };
706
707 } // namespace detail
708
709 bool Task::completed() { return base_type::promise().state() == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE; }
710
711 struct device_wait_kernel
712 { };
713
714
715 /* NOTE: below is preliminary for reductions on the device, which is not available yet */
716#if 0
717 /**************************
718 * Device reduction coros *
719 **************************/
720
721 struct device_reducer_promise_type;
722
723 using device_reducer_handle_type = ttg::coroutine_handle<device_reducer_promise_type>;
724
726 struct device_reducer : public device_reducer_handle_type {
727 using base_type = device_reducer_handle_type;
728
731
732 using promise_type = device_reducer_promise_type;
733
735
736 device_reducer(base_type base) : base_type(std::move(base)) {}
737
738 base_type& handle() { return *this; }
739
741 inline bool ready() {
742 return true;
743 }
744
746 inline bool completed();
747 };
748
749
750 /* The promise type that stores the views provided by the
751 * application task coroutine on the first co_yield. It subsequently
752 * tracks the state of the task when it moves from waiting for transfers
753 * to waiting for the submitted kernel to complete. */
754 struct device_reducer_promise_type {
755
756 /* do not suspend the coroutine on first invocation, we want to run
757 * the coroutine immediately and suspend when we get the device transfers.
758 */
759 ttg::suspend_never initial_suspend() {
760 m_state = ttg::device::detail::TTG_DEVICE_CORO_INIT;
761 return {};
762 }
763
764 /* suspend the coroutine at the end of the execution
765 * so we can access the promise.
766 * TODO: necessary? maybe we can save one suspend here
767 */
768 ttg::suspend_always final_suspend() noexcept {
769 m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
770 return {};
771 }
772
773 template<typename... Ts>
774 ttg::suspend_always await_transform(detail::to_device_t<Ts...>&& a) {
775 bool need_transfer = !(TTG_IMPL_NS::register_device_memory(a.ties));
776 /* TODO: are we allowed to not suspend here and launch the kernel directly? */
777 m_state = ttg::device::detail::TTG_DEVICE_CORO_WAIT_TRANSFER;
778 return {};
779 }
780
781 void return_void() {
782 m_state = ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
783 }
784
785 bool complete() const {
786 return m_state == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE;
787 }
788
789 device_reducer get_return_object() { return device_reducer{device_reducer_handle_type::from_promise(*this)}; }
790
791 void unhandled_exception() { }
792
793 auto state() {
794 return m_state;
795 }
796
797
798 private:
799 ttg::device::detail::ttg_device_coro_state m_state = ttg::device::detail::TTG_DEVICE_CORO_STATE_NONE;
800
801 };
802
803 bool device_reducer::completed() { return base_type::promise().state() == ttg::device::detail::TTG_DEVICE_CORO_COMPLETE; }
804#endif // 0
805
806} // namespace ttg::device
807
808#endif // TTG_HAVE_COROUTINE
809
810#endif // TTG_DEVICE_TASK_H
std::enable_if_t<!meta::is_void_v< Key > &&meta::is_void_v< Value >, void > sendk(const Key &key)
Definition terminal.h:476
std::enable_if_t< meta::is_all_void_v< Key, Value >, void > send()
Definition terminal.h:515
A complete version of void.
Definition void.h:12
constexpr auto data(C &c) -> decltype(c.data())
Definition span.h:191
std::integral_constant< bool,(Flags &const_) !=0 > is_const
STL namespace.
void broadcast(const std::tuple< RangesT... > &keylists, valueT &&value, std::tuple< ttg::Out< out_keysT, out_valuesT >... > &t)
Definition func.h:348
constexpr auto get(typelist< T, RestOfTs... >)
Definition typelist.h:102
TTG_CXX_COROUTINE_NAMESPACE::suspend_always suspend_always
Definition coroutine.h:22
void send(const keyT &key, valueT &&value, ttg::Out< keyT, valueT > &t)
Sends a task id and a value to the given output terminal.
Definition func.h:159
constexpr const ttg::Runtime ttg_runtime
Definition import.h:21
Runtime
Definition runtimes.h:16
TTG_CXX_COROUTINE_NAMESPACE::suspend_never suspend_never
Definition coroutine.h:23
TTG_CXX_COROUTINE_NAMESPACE::coroutine_handle< Promise > coroutine_handle
Definition coroutine.h:25
void sendk(const keyT &key, ttg::Out< keyT, void > &t)
Sends a task id (without an accompanying value) to the given output terminal.
Definition func.h:170
void sendv(valueT &&value, ttg::Out< void, valueT > &t)
Sends a value (without an accompanying task id) to the given output terminal.
Definition func.h:180
void broadcastk(const rangeT &keylist, std::tuple< ttg::Out< out_keysT, out_valuesT >... > &t)
Definition func.h:453