kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_when_all.cpp
Go to the documentation of this file.
2PRAGMA_DIAGNOSTIC_PUSH
4#include "exec/single_thread_context.hpp"
5PRAGMA_DIAGNOSTIC_POP
6
10
13
27
39
40namespace Tests::GraphImpl {
41
42using namespace Kokkos::utils::callbacks;
43
69
71consteval bool test_sndr_traits() {
73 using schd_sndr_t = typename TEST_CATEGORY(WhenAllTest)::schedule_sender_t;
74
76 using functor_t = Tests::Utils::Functors::Increment<TEST_CATEGORY(WhenAllTest)::view_s_t, true>;
77 using then_sndr_t = decltype(stdexec::then(std::declval<schd_sndr_t>(), std::declval<functor_t>()));
78
80 using when_all_sndr_t = stdexec::transform_sender_result_t<
81 decltype(stdexec::when_all(std::declval<then_sndr_t>(), std::declval<then_sndr_t>())),
82 stdexec::env<>
83 >;
84
87
90
91 return true;
92}
93static_assert(test_sndr_traits());
94
96consteval bool test_sndr_nothrow_transformable() {
97 using when_all_sndr_t = decltype(stdexec::when_all(
98 stdexec::schedule(std::declval<typename TEST_CATEGORY(WhenAllTest)::scheduler_t>())
100
101 static_assert(std::same_as<
102 stdexec::__demangle_t<when_all_sndr_t>,
104 stdexec::when_all_t,
105 stdexec::__,
107 stdexec::then_t,
109 typename TEST_CATEGORY(WhenAllTest)::schedule_sender_t
110 >
111 >
112 >);
113
114 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
116 stdexec::set_value_t,
117 when_all_sndr_t&&,
118 stdexec::env<>
119 >);
120
121 using when_all_maythrow_on_move_sndr_t = decltype(stdexec::when_all(
122 stdexec::schedule(std::declval<typename TEST_CATEGORY(WhenAllTest)::scheduler_t>())
124
125 static_assert(!stdexec::__detail::__has_nothrow_transform_sender<
126 Kokkos::Execution::GraphImpl::Domain,
127 stdexec::set_value_t,
128 when_all_maythrow_on_move_sndr_t&&,
129 stdexec::env<>
130 >);
131
132 return true;
133}
134static_assert(test_sndr_nothrow_transformable());
135
137consteval bool test_sndr_nothrow_connectable() {
139 static_assert(!std::is_nothrow_constructible_v<Kokkos::Experimental::Graph<TEST_EXECUTION_SPACE>>);
140
141 using when_all_sndr_t = decltype(stdexec::when_all(
142 stdexec::schedule(std::declval<typename TEST_CATEGORY(WhenAllTest)::scheduler_t>())
143 | stdexec::then(Tests::Utils::Functors::NoOp<false, false, false>{})));
144
145 static_assert(!stdexec::__nothrow_connectable<when_all_sndr_t, Tests::Utils::SinkReceiver>);
146
147 return true;
148}
149static_assert(test_sndr_nothrow_connectable());
150
155template <typename ExecA, typename ExecB>
157 if constexpr (std::same_as<ExecA, ExecB>)
158 return true;
159 else {
160 using sndr_t = stdexec::transform_sender_result_t<
161 decltype(stdexec::when_all(
162 stdexec::schedule(Kokkos::Execution::GraphContext{std::declval<ExecA>()}.get_scheduler()),
163 stdexec::schedule(Kokkos::Execution::GraphContext{std::declval<ExecB>()}.get_scheduler()))),
165 >;
166
167 static_assert(
168 std::same_as<
169 sndr_t,
170 stdexec::__not_a_sender<
171 stdexec::_WHAT_(Kokkos::Execution::GraphImpl::CANNOT_DISPATCH_THIS_ALGORITHM_TO_THE_GRAPH_SCHEDULER),
172 stdexec::_WHY_(Kokkos::Execution::GraphImpl::BECAUSE_THE_EXECUTION_SPACE_TYPE_IS_NOT_HOMOGENEOUS),
173 stdexec::_WHERE_(stdexec::_IN_ALGORITHM_, stdexec::when_all_t),
175 1,
177 >,
178 stdexec::_WITH_SENDERS_<
181 >,
182 stdexec::_WITH_ENVIRONMENT_(Tests::Utils::SinkReceiver)
183 >
184 >);
185
186 return true;
187 }
188}
189static_assert(test_sndr_cannot_mix_execution_space_type<TEST_EXECUTION_SPACE, Kokkos::DefaultHostExecutionSpace>());
190
195TEST_F(TEST_CATEGORY(WhenAllTest), one_branch) {
196 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
197
198 const context_t gctx{exec};
199
200 auto sndr = stdexec::when_all(stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT(data));
201
202 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
203
205
206 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
207
208 ASSERT_THAT(
209 recorded_events,
210 testing::ElementsAre(
212 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
214 recorded_events.at(0), MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1))),
215 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
216 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
217
218 ASSERT_EQ(data(), 1);
219}
220
225TEST_F(TEST_CATEGORY(WhenAllTest), two_branches) {
226 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
227
228 const context_t gctx{exec};
229
230 auto branch_a = stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT_ATOMIC(Device, data);
231 auto branch_b = stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT_ATOMIC(Device, data);
232
233 auto sndr = stdexec::when_all(std::move(branch_a), std::move(branch_b));
234
235 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
236
238
239 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
240
241 ASSERT_THAT(
242 recorded_events,
243 testing::ElementsAre(
245 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
246 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
248 recorded_events.at(0),
249 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1)),
250 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(2))),
251 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
252 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
253
254 ASSERT_EQ(data(), 2);
255}
256
261TEST_F(TEST_CATEGORY(WhenAllTest), three_branches) {
262 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
263
264 const context_t gctx{exec};
265
266 auto branch_a = stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT_ATOMIC(Device, data);
267 auto branch_b = stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT_ATOMIC(Device, data);
268 auto branch_c = stdexec::schedule(gctx.get_scheduler()) | THEN_INCREMENT_ATOMIC(Device, data);
269
270 auto sndr = stdexec::when_all(std::move(branch_a), std::move(branch_b), std::move(branch_c));
271
272 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
273
275
276 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
277
278 ASSERT_THAT(
279 recorded_events,
280 testing::ElementsAre(
282 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
283 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
284 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
286 recorded_events.at(0),
287 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1)),
288 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(2)),
289 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(3))),
290 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
291 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
292
293 ASSERT_EQ(data(), 3);
294}
295
304TEST_F(TEST_CATEGORY(WhenAllTest), three_branches_starting_on_single_thread_context) {
305 const Kokkos::View<value_t[3], Kokkos::SharedSpace> data_per_branch(Kokkos::view_alloc("data - shared space"));
306
307 experimental::execution::single_thread_context stc_a{}, stc_b{}, stc_c{};
308
309 const context_t gctx{exec};
310
313
314 auto branch_a = stdexec::schedule(stc_a.get_scheduler())
315 | stdexec::then(functor_h_t{.prev = 0, .value = 2, .data = &data_per_branch(0)})
316 | stdexec::continues_on(gctx.get_scheduler())
317 | stdexec::then(functor_d_t{.prev = 2, .value = 3, .data = &data_per_branch(0)});
318 auto branch_b = stdexec::schedule(stc_b.get_scheduler())
319 | stdexec::then(functor_h_t{.prev = 0, .value = 3, .data = &data_per_branch(1)})
320 | stdexec::continues_on(gctx.get_scheduler())
321 | stdexec::then(functor_d_t{.prev = 3, .value = 4, .data = &data_per_branch(1)});
322 auto branch_c = stdexec::schedule(stc_c.get_scheduler())
323 | stdexec::then(functor_h_t{.prev = 0, .value = 4, .data = &data_per_branch(2)})
324 | stdexec::continues_on(gctx.get_scheduler())
325 | stdexec::then(functor_d_t{.prev = 4, .value = 5, .data = &data_per_branch(2)});
326
327 auto sndr = stdexec::when_all(
328 std::move(branch_a), std::move(branch_b), std::move(branch_c)); // NOLINT(performance-move-const-arg)
329
330 ASSERT_THAT(Tests::Utils::span_from(data_per_branch), testing::Each(testing::Eq(0)))
331 << "Eager execution is not " "allowed.";
332
334
336
337 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(
338 std::move(sndr)); // NOLINT(performance-move-const-arg)
339
340 ASSERT_THAT(
341 recorded_events,
342 testing::ElementsAre(
344 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
345 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
346 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
348 recorded_events.at(0),
349 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1)),
350 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(2)),
351 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(3))),
352 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
353 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
354
355 ASSERT_THAT(Tests::Utils::span_from(data_per_branch), testing::ElementsAre(5, 7, 9));
356}
357
363TEST_F(TEST_CATEGORY(WhenAllTest), three_branches_some_starting_on_single_thread_context) {
364 const Kokkos::View<value_t[3], Kokkos::SharedSpace> data_per_branch(Kokkos::view_alloc("data - shared space"));
365
366 experimental::execution::single_thread_context stc_a{}, stc_b{};
367
368 const context_t gctx{exec};
369
372
373 auto branch_a = stdexec::schedule(stc_a.get_scheduler())
374 | stdexec::then(functor_h_t{.prev = 0, .value = 2, .data = &data_per_branch(0)})
375 | stdexec::continues_on(gctx.get_scheduler())
376 | stdexec::then(functor_d_t{.prev = 2, .value = 3, .data = &data_per_branch(0)});
377 auto branch_b = stdexec::schedule(stc_b.get_scheduler())
378 | stdexec::then(functor_h_t{.prev = 0, .value = 3, .data = &data_per_branch(1)})
379 | stdexec::continues_on(gctx.get_scheduler())
380 | stdexec::then(functor_d_t{.prev = 3, .value = 4, .data = &data_per_branch(1)});
381 auto branch_c = stdexec::schedule(gctx.get_scheduler())
382 | stdexec::then(functor_d_t{.prev = 0, .value = 4, .data = &data_per_branch(2)});
383
384 auto sndr = stdexec::when_all(
385 std::move(branch_a), std::move(branch_b), std::move(branch_c)); // NOLINT(performance-move-const-arg)
386
387 ASSERT_THAT(Tests::Utils::span_from(data_per_branch), testing::Each(testing::Eq(0)))
388 << "Eager execution is not " "allowed.";
389
391
393
394 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(
395 std::move(sndr)); // NOLINT(performance-move-const-arg)
396
397 ASSERT_THAT(
398 recorded_events,
399 testing::ElementsAre(
401 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
402 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
403 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
405 recorded_events.at(0),
406 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1)),
407 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(2)),
408 MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(3))),
409 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
410 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
411
412 ASSERT_THAT(Tests::Utils::span_from(data_per_branch), testing::ElementsAre(5, 7, 4));
413}
414
416TEST_F(TEST_CATEGORY(WhenAllTest), forwarding_env) {
417 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
418
419 std::atomic<size_t> count = 0;
420
421 int value;
422
423 const context_t gctx{exec};
424
425 stdexec::sender auto sndr =
426 stdexec::when_all(
427 stdexec::read_env(stdexec::get_allocator)
428 | stdexec::then([&value](auto allocator) { value = Tests::Utils::round_trip_allocate(allocator, 42); })
429 | stdexec::continues_on(gctx.get_scheduler())
431 | stdexec::write_env(stdexec::prop{stdexec::get_allocator, Tests::Utils::TrackingAllocator<int>{&count}});
432
433 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
434
436
437 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
438
439 ASSERT_THAT(
440 recorded_events,
441 testing::ElementsAre(
443 MATCHER_FOR_GRAPH_ADDNODE(recorded_events.at(0), device_handle, nullptr),
445 recorded_events.at(0), MATCHER_FOR_GRAPH_NODE_OF(recorded_events.at(1))),
446 MATCHER_FOR_GRAPH_SUBMIT(TEST_EXECUTION_SPACE{}, recorded_events.at(0)),
447 MATCHER_FOR_BEGIN_FENCE(TEST_EXECUTION_SPACE{}, dispatch_label(TEST_EXECUTION_SPACE{}, "after dispatch"))));
448
449 ASSERT_EQ(data(), 1);
450
451 ASSERT_EQ(value, 42);
452 ASSERT_EQ(count, 1);
453}
454
455} // namespace Tests::GraphImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
#define TEST_CATEGORY(_name_)
Definition category.hpp:10
device_handle_t default_device_handle
RecorderListener< ConjunctionMatcher< EventDiscardMatcher< TEST_EXECUTION_SPACE >, GraphEventDiscardMatcher< TEST_EXECUTION_SPACE > >, BeginFenceEvent, BeginParallelForEvent, AllocateDataEvent, DeallocateDataEvent, Kokkos::Execution::Impl::RecordEvent, Kokkos::Execution::Impl::WaitEvent, Kokkos::Execution::GraphImpl::GraphAddAggregateNodeEvent, Kokkos::Execution::GraphImpl::GraphAddNodeEvent, Kokkos::Execution::GraphImpl::GraphCreateEvent, Kokkos::Execution::GraphImpl::GraphInstantiateEvent, Kokkos::Execution::GraphImpl::GraphSubmitEvent > recorder_listener_t
Concept for a sender whose completion scheduler is Kokkos::Execution::GraphImpl::Scheduler.
Concept that constrains the type of a sender that dispatches a functor for execution.
#define KOKKOS_EXECUTION_THREADS_THROWS_ON_SYNC_WAIT_ASSERT_AND_SKIP(_sndr_)
Definition context.hpp:69
#define KOKKOS_EXECUTION_TEST_UTILS_GRAPH_FENCE(_exec_)
#define KOKKOS_EXECUTION_STDEXEC_PRAGMA_DIAGNOSTIC_IGNORED
Basic list of ignored diagnostics when including anything from stdexec.
#define THEN_INCREMENT(_data_)
Add a then using Tests::Utils::Functors::Increment that may throw. // NOLINTNEXTLINE(cppcoreguideline...
Definition increment.hpp:59
#define THEN_INCREMENT_ATOMIC(_scope_, _data_)
Same as THEN_INCREMENT, using Tests::Utils::atomic_fetch_add. // NOLINTNEXTLINE(cppcoreguidelines-mac...
Definition increment.hpp:63
consteval bool test_sndr_nothrow_transformable()
Definition test_bulk.cpp:83
consteval bool test_sndr_traits()
Definition test_bulk.cpp:57
consteval bool test_sndr_nothrow_connectable()
consteval bool test_sndr_cannot_mix_execution_space_type()
typename stdexec::__basic_sender< Args... >::type basic_sender_t
See https://github.com/NVIDIA/stdexec/pull/1873#discussion_r2834863237.
Definition stdexec.hpp:12
auto record_sync_wait(Sndr &&sndr)
Definition sync_wait.hpp:14
constexpr check_rcvr_env_queryable_with_t< true, Queries... > check_rcvr_env_queryable_with
auto span_from(const ViewType &view) noexcept
Get a std::span from a rank-one Kokkos::View.
Definition kokkos.hpp:41
auto round_trip_allocate(Allocator &allocator, T &&value)
Execution context using Kokkos::Experimental::Graph under the hood.
Definition graph.hpp:117
auto get_scheduler() const noexcept -> GraphImpl::Scheduler< Exec >
Definition graph.hpp:126
Event to be sent to Kokkos::utils::callbacks::dispatch when a Kokkos graph aggregate node is added.
Definition events.hpp:50
Event to be sent to Kokkos::utils::callbacks::dispatch when a Kokkos graph node is added.
Definition events.hpp:35
Event to be sent to Kokkos::utils::callbacks::dispatch when a Kokkos graph is created.
Definition events.hpp:16
Event to be sent to Kokkos::utils::callbacks::dispatch when a Kokkos graph is instantiated.
Definition events.hpp:65
Event to be sent to Kokkos::utils::callbacks::dispatch when a Kokkos graph is submitted.
Definition events.hpp:76
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:54
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:75
Similar to EventDiscardMatcher, for graph-related events.
Definition events.hpp:76
Kokkos::View< value_t, Kokkos::SharedSpace > view_s_t
Definition context.hpp:32
Load the value at data and check it is equal to prev. Then, add value to it.
Kokkos::Impl::DeviceHandle< Exec > device_handle_t
A receiver that can handle all completions and does nothing with them.
A minimal tracking allocator.
#define MATCHER_FOR_GRAPH_CREATE(_device_handle_)
Definition events.hpp:17
#define MATCHER_FOR_GRAPH_SUBMIT(_exec_, _graph_create_event_variant_)
Definition events.hpp:63
#define MATCHER_FOR_GRAPH_ADDNODE(_graph_create_event_variant_, _device_handle_, _predecessor_)
Definition events.hpp:24
#define MATCHER_FOR_GRAPH_ADD_AGGREGATE_NODE(_graph_create_event_variant_,...)
Definition events.hpp:36
#define MATCHER_FOR_GRAPH_NODE_OF(_graph_add_node_event_)
Definition events.hpp:47