kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_continues_on.cpp
Go to the documentation of this file.
3
14
26
27using host_execution_space = Kokkos::DefaultHostExecutionSpace;
28
30
31using namespace Kokkos::utils::callbacks;
32
45
48 using schd_t = typename ContinuesOnTest::scheduler_t;
49 using schd_sndr_t = typename ContinuesOnTest::schedule_sender_t;
50
53 schd_t,
54 schd_sndr_t
55 >;
56
57 static_assert(stdexec::__nothrow_connectable<schedule_from_sndr_t, Tests::Utils::SinkReceiver>);
58
59 return true;
60}
61static_assert(test_schedule_from_sndr_traits());
62
69static_assert(test_continues_on_sndr_traits());
70
72TEST_F(ContinuesOnTest, queryable_get_exec) {
73 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
74
75 const host_execution_space exec_h{};
76
78 const context_t esc_A{exec_A}, esc_B{exec_B};
79
80 auto schs_A = stdexec::schedule(esc_A.get_scheduler());
81
83 static_assert(!stdexec::__queryable_with<decltype(stdexec::get_env(schs_A)), Kokkos::Execution::Impl::get_exec_t>);
84
85 auto schs_A_then = std::move(schs_A) | THEN_LABELED('A'); // NOLINT(performance-move-const-arg)
86
87 const auto sch_B = esc_B.get_scheduler();
88 auto schs_A_then_con_B = std::move(schs_A_then) // NOLINT(performance-move-const-arg)
89 | stdexec::continues_on(sch_B);
90
92 ASSERT_EQ(stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B)), sch_B);
93
94 auto schs_A_then_con_B_then = std::move(schs_A_then_con_B) // NOLINT(performance-move-const-arg)
95 | THEN_LABELED('B');
96
97 static_assert(std::same_as<
98 stdexec::__demangle_t<decltype(schs_A_then_con_B_then)>,
100 stdexec::then_t,
103 stdexec::continues_on_t,
106 stdexec::schedule_from_t,
107 stdexec::__,
109 stdexec::then_t,
112 >
113 >
114 >
115 >
116 >);
117
119 auto sch_h = esc_h.get_scheduler();
120 auto schs_A_then_con_B_then_con_h_then = std::move(schs_A_then_con_B_then) // NOLINT(performance-move-const-arg)
121 | stdexec::continues_on(sch_h) | THEN_LABELED('h');
122 ASSERT_EQ(
123 stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B_then_con_h_then)),
124 sch_h);
125
126 auto op_state = stdexec::connect(
127 std::move(schs_A_then_con_B_then_con_h_then), // NOLINT(performance-move-const-arg)
129 .state = std::addressof(esc_h.m_state), .runloop_state = nullptr, .result = nullptr});
130
138 std::string_view,
140 Kokkos::RangePolicy<host_execution_space, Kokkos::LaunchBounds<1>>
141 >
142 >>;
143 static_assert(std::same_as<decltype(op_state.inner_opstate.completion_signal.rcvr.rcvr.rcvr), then_rcvr_t>);
144 static_assert(!stdexec::__queryable_with<stdexec::env_of_t<then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
145
150 then_rcvr_t
151 >;
152 static_assert(std::same_as<decltype(op_state.inner_opstate.completion_signal.rcvr.rcvr), con_h_then_rcvr_t>);
153 static_assert(stdexec::__queryable_with<stdexec::env_of_t<con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
154 static_assert(std::same_as<
155 stdexec::env_of_t<con_h_then_rcvr_t>,
156 stdexec::env<
157 stdexec::prop<
160 >,
161 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
162 >
163 >);
164 ASSERT_EQ(
165 Kokkos::Execution::Impl::get_exec(stdexec::get_env(op_state.inner_opstate.completion_signal.rcvr.rcvr)).get(),
166 exec_h);
167
173 con_h_then_rcvr_t
174 >;
175 static_assert(std::same_as<decltype(op_state.inner_opstate.completion_signal.rcvr), sfrom_con_h_then_rcvr_t>);
176 static_assert(
177 !stdexec::__queryable_with<stdexec::env_of_t<sfrom_con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
178
179 using then_sfrom_con_h_then_rcvr_t =
181 sfrom_con_h_then_rcvr_t,
183 std::string_view,
185 Kokkos::RangePolicy<TEST_EXECUTION_SPACE, Kokkos::LaunchBounds<1>>
186 >
187 >>;
188 static_assert(std::same_as<
189 decltype(op_state.inner_opstate.inner_opstate.completion_signal.rcvr.rcvr.rcvr),
190 then_sfrom_con_h_then_rcvr_t
191 >);
192 static_assert(!stdexec::__queryable_with<
193 stdexec::env_of_t<then_sfrom_con_h_then_rcvr_t>,
195 >);
196
197 using con_B_then_sfrom_con_h_then_rcvr_t = Kokkos::Execution::ExecutionSpaceImpl::ContinuesOnReceiver<
200 then_sfrom_con_h_then_rcvr_t
201 >;
202 static_assert(std::same_as<
203 decltype(op_state.inner_opstate.inner_opstate.completion_signal.rcvr.rcvr),
204 con_B_then_sfrom_con_h_then_rcvr_t
205 >);
206 static_assert(stdexec::__queryable_with<
207 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
209 >);
210 static_assert(std::same_as<
211 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
212 stdexec::env<
213 stdexec::prop<
216 >,
217 stdexec::__env::__fwd<stdexec::env<
218 stdexec::prop<
221 >,
222 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
223 >>
224 >
225 >);
226 ASSERT_EQ(
228 stdexec::get_env(op_state.inner_opstate.inner_opstate.completion_signal.rcvr.rcvr))
229 .get(),
230 exec_B);
231
232 using sfrom_con_B_then_sfrom_con_h_then_rcvr_t = Kokkos::Execution::ExecutionSpaceImpl::ScheduleFromReceiver<
236 con_B_then_sfrom_con_h_then_rcvr_t
237 >;
238 static_assert(std::same_as<
239 decltype(op_state.inner_opstate.inner_opstate.completion_signal.rcvr),
240 sfrom_con_B_then_sfrom_con_h_then_rcvr_t
241 >);
242 static_assert(!stdexec::__queryable_with<
243 stdexec::env_of_t<sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
245 >);
246
247 using then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t =
249 sfrom_con_B_then_sfrom_con_h_then_rcvr_t,
251 std::string_view,
253 Kokkos::RangePolicy<TEST_EXECUTION_SPACE, Kokkos::LaunchBounds<1>>
254 >
255 >>;
256 static_assert(std::same_as<
257 decltype(op_state.inner_opstate.inner_opstate.inner_opstate.rcvr),
258 then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t
259 >);
260 static_assert(!stdexec::__queryable_with<
261 stdexec::env_of_t<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
263 >);
264}
265
267TEST_F(ContinuesOnTest, then_sync_wait) {
268 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
269
270 const context_t esc{exec};
271
272 stdexec::sender auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
273
274 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
275
276 ASSERT_THAT(
278 ::testing::ElementsAre(
281
282 ASSERT_EQ(data(), 1);
283}
284
291TEST_F(ContinuesOnTest, transition_to_same_execution_space_instance) {
292 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
293
294 const context_t esc{exec};
295
296 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
297 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
298 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
299
300 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
301
302 ASSERT_THAT(
304 ::testing::ElementsAre(
309
310 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
311}
312
317TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_same_type) {
318 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
319
320 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
321
322 const context_t esc_A{exec_A};
323 Tests::Utils::show_exec_space_id(exec_A, "exec_A");
324 const context_t esc_B{exec_B};
325 Tests::Utils::show_exec_space_id(exec_B, "exec_B");
326
327 auto chain = stdexec::just() | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data)
328 | stdexec::continues_on(esc_B.get_scheduler()) | THEN_INCREMENT(data)
329 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
330
331 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
332
333 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
334
335 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
336 ASSERT_THAT(
337 recorded_events,
338 ::testing::ElementsAre(
342 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
343 } else {
344 ASSERT_THAT(
345 recorded_events,
346 ::testing::ElementsAre(
348 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "schedule_from")),
350 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec, "schedule_from")),
352 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
353 }
354
355 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
356}
357
362TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_different_type) {
363 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
364
365 const host_execution_space exec_h{};
366
368 const context_t esc{exec};
369
371 Tests::Utils::show_exec_space_id(exec_h, "exec_h");
372
373 using level_C_env_t = Kokkos::Execution::Impl::SyncWait::env;
374 using level_B_env_t = stdexec::__env::__fwd<stdexec::env<
375 stdexec::prop<
378 >,
379 stdexec::__env::__fwd<level_C_env_t>
380 >>;
381 using level_A_env_t = stdexec::__env::__fwd<stdexec::env<
382 stdexec::prop<
385 >,
386 level_B_env_t
387 >>;
388 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
389 | Tests::Utils::check_rcvr_env<level_A_env_t>() | stdexec::continues_on(esc_h.get_scheduler())
391 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
393
394 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
395
396 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
397
399 ASSERT_THAT(
400 recorded_events,
401 ::testing::ElementsAre(
403 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
406 } else {
407 ASSERT_THAT(
408 recorded_events,
409 ::testing::ElementsAre(
412 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
413 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "schedule_from")),
416 }
417
418 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
419}
420
422consteval bool test_sndr_nothrow_transformable() {
423 using sndr_continues_on_t =
424 decltype(stdexec::just() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()));
425
426 static_assert(std::same_as<
427 stdexec::__demangle_t<sndr_continues_on_t>,
429 stdexec::continues_on_t,
432 stdexec::schedule_from_t,
433 stdexec::__,
435 >
436 >
437 >);
438
439 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
441 stdexec::set_value_t,
442 sndr_continues_on_t&&,
443 stdexec::env<>
444 >);
445
446 using sndr_schedule_from_t = decltype(stdexec::schedule_from(
447 stdexec::schedule(std::declval<typename ContinuesOnTest::scheduler_t>())));
448
449 static_assert(std::same_as<
450 stdexec::__demangle_t<sndr_schedule_from_t>,
452 stdexec::schedule_from_t,
453 stdexec::__,
455 >
456 >);
457
458 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
460 stdexec::set_value_t,
461 sndr_schedule_from_t&&,
462 stdexec::env<>
463 >);
464
465 return true;
466}
467static_assert(test_sndr_nothrow_transformable());
468
469} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_BEGIN_PFOR(_exec_, _label_)
#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
#define THEN_INCREMENT(_data_)
Add a then using Tests::Utils::Functors::Increment that may throw. // NOLINTNEXTLINE(cppcoreguideline...
Definition increment.hpp:35
#define THEN_LABELED(_id_)
Add a then using Tests::Utils::Functors::Labeled. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage).
Definition labeled.hpp:18
constexpr get_exec_t get_exec
Definition get_exec.hpp:17
consteval bool test_schedule_from_sndr_traits()
consteval bool test_sndr_nothrow_transformable()
Definition test_bulk.cpp:83
consteval bool test_continues_on_sndr_traits()
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
consteval bool check_continues_on()
Check how the scheduler customizes stdexec::continues_on.
void show_exec_space_id(const Exec &exec, std::string_view label="", std::ostream &out=std::cout)
Definition kokkos.hpp:33
constexpr check_rcvr_env_t< ExpectedEnv > check_rcvr_env
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 >
Scheduler for a Kokkos execution space.
Inspired by https://github.com/kokkos/kokkos/blob/69273c3a4e7b6adeb95066341ca201d62fe1e698/core/src/i...
Definition then.hpp:16
Wrap a Kokkos execution space to make it cheap to copy/move in new environments.
Definition get_exec.hpp:27
Receiver for an object parent_op that implements complete.
Definition receiver.hpp:12
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:52
Receiver for stdexec::sync_wait.
Definition sync_wait.hpp:46
Inspired by https://github.com/NVIDIA/stdexec/blob/16076a81efa4477513e6ede9c2741fd034ecef99/include/s...
Definition sync_wait.hpp:15
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:73
decltype(std::declval< const context_t >().get_scheduler()) scheduler_t
Definition context.hpp:28
decltype(stdexec::schedule(std::declval< scheduler_t >())) schedule_sender_t
Definition context.hpp:29
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:27
Kokkos::DefaultHostExecutionSpace host_execution_space