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
9
11
22
34
35using host_execution_space = Kokkos::DefaultHostExecutionSpace;
36
38
39using namespace Kokkos::utils::callbacks;
40
53
61TEST_F(WhenAllTest, single_branch) {
62 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
63
64 const context_t esc{exec};
65
66 auto sndr = stdexec::when_all(stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT(data));
67
68 static_assert(std::same_as<
69 decltype(stdexec::get_completion_domain<stdexec::set_value_t>(stdexec::get_env(sndr))),
71 >);
72
76
77 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
78
79 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
80
83 ASSERT_THAT(recorded_events, [&]() {
85 return testing::ElementsAre(
88 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
89 } else {
90 return testing::ElementsAre(
92 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
93 }
94 }());
95
96 ASSERT_EQ(data(), 1);
97}
98
107TEST_F(WhenAllTest, single_branch_followed_by_self) {
108 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
109
110 const context_t esc{exec};
111
112 auto sndr = stdexec::when_all(stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT(data))
113 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
114
115 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
116
117 ASSERT_THAT(
119 testing::ElementsAre(
123
124 ASSERT_EQ(data(), 2);
125}
126
136TEST_F(WhenAllTest, single_mixed_branch_followed_by_self) {
137 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
138
139 const context_t esc{exec};
140 experimental::execution::single_thread_context stc{};
141
142 auto sndr = stdexec::when_all(
143 stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT(data)
144 | stdexec::continues_on(stc.get_scheduler()) | THEN_INCREMENT(data))
145 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
146
147 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
148
150
151 ASSERT_THAT(
153 testing::ElementsAre(
158
159 ASSERT_EQ(data(), 3);
160}
161
170TEST_F(WhenAllTest, single_branch_followed_by_other_and_finish_on_self) {
171 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
172
173 const context_t esc{exec};
174 experimental::execution::single_thread_context stc{};
175
176 auto sndr = stdexec::when_all(stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT(data))
177 | stdexec::continues_on(stc.get_scheduler()) | THEN_INCREMENT(data)
178 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
179
180 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
181
183
184 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
185
186 ASSERT_THAT(recorded_events, [&]() {
188 return testing::ElementsAre(
191 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)),
194 } else {
195 return testing::ElementsAre(
197 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")),
200 }
201 }());
202
203 ASSERT_EQ(data(), 3);
204}
205
216TEST_F(WhenAllTest, two_mixed_branches_followed_by_self) {
217 const view_s_t data(Kokkos::view_alloc("data - shared space"));
218
219 const context_t esc{exec};
220 experimental::execution::single_thread_context stc{};
221
222 auto w_a = stdexec::when_all(
223 stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data),
224 stdexec::schedule(stc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data)
225 | stdexec::continues_on(esc.get_scheduler()));
226
227 static_assert(
228 std::same_as<
229 decltype(stdexec::get_completion_domain<stdexec::set_value_t>(stdexec::get_env(w_a), stdexec::env<>{})),
231 >);
232
233 auto sndr = std::move(w_a) | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
234
235 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
236
238
239 ASSERT_THAT(
241 testing::ElementsAre(
245
246 ASSERT_EQ(data(), 3);
247}
248
259TEST_F(WhenAllTest, two_branches_followed_by_self) {
260 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
261
262 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
263
264 const context_t esc_A{exec_A}, esc_B{exec_B};
265
266 auto sndr = stdexec::when_all(
267 stdexec::schedule(esc_A.get_scheduler()) | THEN_INCREMENT_ATOMIC(data),
268 stdexec::schedule(esc_B.get_scheduler()) | THEN_INCREMENT_ATOMIC(data))
269 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
270
271 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
272
273 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
274
275 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
276 ASSERT_THAT(
277 recorded_events,
278 testing::ElementsAre(
279 MATCHER_FOR_BEGIN_PFOR(exec_A, dispatch_label(exec_A, "then")),
280 MATCHER_FOR_BEGIN_PFOR(exec_B, dispatch_label(exec_B, "then")),
281 MATCHER_FOR_BEGIN_PFOR(exec_A, dispatch_label(exec_A, "then")),
282 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec_A, "sync_wait"))));
283 } else {
284 ASSERT_THAT(recorded_events, [&]() {
286 return testing::ElementsAre(
290 MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)),
292 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait")));
293
294 } else {
295 return testing::ElementsAre(
296 MATCHER_FOR_BEGIN_PFOR(exec_A, dispatch_label(exec_A, "then")),
297 MATCHER_FOR_BEGIN_PFOR(exec_B, dispatch_label(exec_B, "then")),
298 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec_B, "after dispatch")),
299 MATCHER_FOR_BEGIN_PFOR(exec_A, dispatch_label(exec_A, "then")),
300 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec_A, "sync_wait")));
301 }
302 }());
303 }
304
305 ASSERT_EQ(data(), 3);
306}
307
319TEST_F(WhenAllTest, two_branches_host_device_followed_by_device) {
321
322 const view_s_t data(Kokkos::view_alloc("data - shared space"));
323
324 const context_t esc{exec};
325
326 const host_execution_space exec_h{};
327 const context_h_t esc_h{exec_h};
328
329 auto sndr = stdexec::when_all(
330 stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data),
331 stdexec::schedule(esc_h.get_scheduler()) | THEN_INCREMENT_ATOMIC(data))
332 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
333
334 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
335
336 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
337
339 ASSERT_THAT(
340 recorded_events,
341 testing::ElementsAre(
343 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
346 } else {
347 ASSERT_THAT(recorded_events, [&]() {
349 return testing::ElementsAre(
351 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
353 MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)),
356 } else {
357 return testing::ElementsAre(
359 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
360 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "after dispatch")),
363 }
364 }());
365 }
366
367 ASSERT_EQ(data(), 3);
368}
369
381TEST_F(WhenAllTest, two_mixed_branches_followed_by_other_and_finish_on_self) {
382 const view_s_t data(Kokkos::view_alloc("data - shared space"));
383
384 const context_t esc{exec};
385 experimental::execution::single_thread_context stc{};
386
387 auto sndr = stdexec::when_all(
388 stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data)
389 | stdexec::continues_on(stc.get_scheduler()),
390 stdexec::schedule(stc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data))
391 | stdexec::continues_on(stc.get_scheduler()) | THEN_INCREMENT(data)
392 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
393
394 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
395
397
398 ASSERT_THAT(
400 testing::ElementsAre(
405
406 ASSERT_EQ(data(), 4);
407}
408
423TEST_F(WhenAllTest, nested_with_inner_followed_by_other) {
424 const view_s_t data(Kokkos::view_alloc("data - shared space"));
425
426 const context_t esc{exec};
427 experimental::execution::single_thread_context stc{};
428
429 auto sndr = stdexec::when_all(
430 stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data),
431 stdexec::when_all(stdexec::schedule(esc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data))
433 | stdexec::continues_on(stc.get_scheduler()) | THEN_INCREMENT_ATOMIC(data)
434 | stdexec::continues_on(esc.get_scheduler()))
435 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
436
437 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
438
440
441 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
442
443 ASSERT_THAT(recorded_events, [&]() {
445 return testing::ElementsAre(
449 MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)),
452 } else {
453 return testing::ElementsAre(
456 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")),
459 }
460 }());
461
462 ASSERT_EQ(data(), 4);
463}
464
476TEST_F(WhenAllTest, nested_when_all_with_independent_branch) {
477 const auto [exec_A, exec_B, exec_C] = Kokkos::Experimental::partition_space(exec, 1, 1, 1);
478
479 const context_t esc_A{exec_A}, esc_B{exec_B}, esc_C{exec_C};
480
481 auto br_A = ::stdexec::schedule(esc_A.get_scheduler()) | THEN_LABELED_PFOR(TEST_EXECUTION_SPACE, 'A');
482 auto br_B = ::stdexec::schedule(esc_B.get_scheduler()) | THEN_LABELED_PFOR(TEST_EXECUTION_SPACE, 'B');
483 auto br_C = ::stdexec::schedule(esc_C.get_scheduler()) | THEN_LABELED_PFOR(TEST_EXECUTION_SPACE, 'C');
484
485 auto when_AB_then_D = ::stdexec::when_all(std::move(br_A), std::move(br_B))
486 | ::stdexec::continues_on(esc_A.get_scheduler()) | THEN_LABELED_PFOR(TEST_EXECUTION_SPACE, 'D');
487
488 auto sndr = ::stdexec::when_all(std::move(when_AB_then_D), std::move(br_C));
489
490 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
491
492 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
493 ASSERT_THAT(
494 recorded_events,
495 testing::ElementsAre(
496 MATCHER_FOR_BEGIN_PFOR(exec_A, "'A'"),
497 MATCHER_FOR_BEGIN_PFOR(exec_B, "'B'"),
498 MATCHER_FOR_BEGIN_PFOR(exec_A, "'D'"),
499 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec_A, "after dispatch")),
500 MATCHER_FOR_BEGIN_PFOR(exec_C, "'C'"),
501 MATCHER_FOR_BEGIN_FENCE(exec_C, dispatch_label(exec_C, "after dispatch"))));
502 } else {
503 ASSERT_THAT(recorded_events, [&]() {
505 return testing::ElementsAre(
506 MATCHER_FOR_BEGIN_PFOR(exec_A, "'A'"),
507 MATCHER_FOR_BEGIN_PFOR(exec_B, "'B'"),
509 MATCHER_FOR_BEGIN_PFOR(exec_C, "'C'"),
511 MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)),
512 MATCHER_FOR_BEGIN_PFOR(exec_A, "'D'"),
514 MATCHER_FOR_WAIT_EVENT(recorded_events.at(4)),
515 MATCHER_FOR_WAIT_EVENT(recorded_events.at(7)));
516 } else {
517 return testing::ElementsAre(
518 MATCHER_FOR_BEGIN_PFOR(exec_A, "'A'"),
519 MATCHER_FOR_BEGIN_PFOR(exec_B, "'B'"),
520 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec_B, "after dispatch")),
521 MATCHER_FOR_BEGIN_PFOR(exec_A, "'D'"),
522 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec_A, "after dispatch")),
523 MATCHER_FOR_BEGIN_PFOR(exec_C, "'C'"),
524 MATCHER_FOR_BEGIN_FENCE(exec_C, dispatch_label(exec_C, "after dispatch")));
525 }
526 }());
527 }
528}
529
530} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_WAIT_EVENT(_record_event_variant_)
#define MATCHER_FOR_BEGIN_PFOR(_exec_, _label_)
#define MATCHER_FOR_RECORD_EVENT(_exec_)
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, BeginParallelForEvent, Kokkos::Execution::Impl::RecordEvent, Kokkos::Execution::Impl::WaitEvent > recorder_listener_t
Concept for a sender whose completion scheduler is Kokkos::Execution::ExecutionSpaceImpl::Scheduler.
#define KOKKOS_EXECUTION_THREADS_THROWS_ON_SYNC_WAIT_ASSERT_AND_SKIP(_sndr_)
Definition context.hpp:69
#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:35
#define THEN_INCREMENT_ATOMIC(_data_)
Same as THEN_INCREMENT, using Tests::Utils::atomic_add. // NOLINTNEXTLINE(cppcoreguidelines-macro-usa...
Definition increment.hpp:39
#define THEN_LABELED_PFOR(_exec_, _id_)
Add a Kokkos::Execution::parallel_for using Tests::Utils::Functors::Labeled. // NOLINTNEXTLINE(cppcor...
Definition labeled.hpp:21
constexpr check_rcvr_env_queryable_with_t< false, Queries... > check_rcvr_env_not_queryable_with
auto record_sync_wait(Sndr &&sndr)
Definition sync_wait.hpp:14
bool are_same_instances(const Exec &exec, const OtherExec &other_exec)
Definition kokkos.hpp:13
Matcher to filter out events that are just noise for tests.
Execution context using a Kokkos execution space under the hood.
auto get_scheduler() const noexcept -> ExecutionSpaceImpl::Scheduler< Exec >
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:52
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:73
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:27
Kokkos::DefaultHostExecutionSpace host_execution_space