kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_continues_on.cpp
Go to the documentation of this file.
3
5
16
28
29using host_execution_space = Kokkos::DefaultHostExecutionSpace;
30
32
33using namespace Kokkos::utils::callbacks;
34
47
50 using schd_sndr_t = typename ContinuesOnTest::schedule_sender_t;
51
53
54 static_assert(stdexec::__nothrow_connectable<schedule_from_sndr_t, Tests::Utils::SinkReceiver>);
55
56 return true;
57}
58static_assert(test_schedule_from_sndr_traits());
59
66static_assert(test_continues_on_sndr_traits());
67
69TEST_F(ContinuesOnTest, queryable_get_exec) {
70 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
71
72 const host_execution_space exec_h{};
73
75 const context_t esc_A{exec_A}, esc_B{exec_B};
76
77 auto schs_A = stdexec::schedule(esc_A.get_scheduler());
78
80 static_assert(!stdexec::__queryable_with<decltype(stdexec::get_env(schs_A)), Kokkos::Execution::Impl::get_exec_t>);
81
82 auto schs_A_then = std::move(schs_A) | THEN_LABELED('A'); // NOLINT(performance-move-const-arg)
83
84 const auto sch_B = esc_B.get_scheduler();
85 auto schs_A_then_con_B = std::move(schs_A_then) // NOLINT(performance-move-const-arg)
86 | stdexec::continues_on(sch_B);
87
89 ASSERT_EQ(stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B)), sch_B);
90
91 auto schs_A_then_con_B_then = std::move(schs_A_then_con_B) // NOLINT(performance-move-const-arg)
92 | THEN_LABELED('B');
93
94 static_assert(std::same_as<
95 stdexec::__demangle_t<decltype(schs_A_then_con_B_then)>,
97 stdexec::then_t,
100 stdexec::continues_on_t,
103 stdexec::schedule_from_t,
104 stdexec::__,
106 stdexec::then_t,
109 >
110 >
111 >
112 >
113 >);
114
116 auto sch_h = esc_h.get_scheduler();
117 auto schs_A_then_con_B_then_con_h_then = std::move(schs_A_then_con_B_then) // NOLINT(performance-move-const-arg)
118 | stdexec::continues_on(sch_h) | THEN_LABELED('h');
119 ASSERT_EQ(
120 stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B_then_con_h_then)),
121 sch_h);
122
123 auto op_state = stdexec::connect(
124 std::move(schs_A_then_con_B_then_con_h_then), // NOLINT(performance-move-const-arg)
126 .state = std::addressof(esc_h.m_state), .runloop_state = nullptr, .result = nullptr});
127
132 const auto& then_opstate = op_state;
136 std::string_view,
138 Kokkos::RangePolicy<host_execution_space, Kokkos::LaunchBounds<1>>
139 >
140 >>;
141 static_assert(std::same_as<decltype(then_opstate.inner_opstate.rcvr), then_rcvr_t>);
142 static_assert(!stdexec::__queryable_with<stdexec::env_of_t<then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
143
145 const auto& con_h_then_opstate = then_opstate.inner_opstate;
146 static_assert(stdexec::__is_instance_of<
147 std::remove_cvref_t<decltype(con_h_then_opstate)>,
149 >);
150 using con_h_then_rcvr_t = decltype(con_h_then_opstate.inner_opstate.rcvr);
151 static_assert(stdexec::__queryable_with<stdexec::env_of_t<con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
152 static_assert(std::same_as<
153 stdexec::env_of_t<con_h_then_rcvr_t>,
154 stdexec::env<
155 stdexec::prop<
158 >,
159 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
160 >
161 >);
162 ASSERT_EQ(Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_h_then_opstate.inner_opstate.rcvr)).get(), exec_h);
163
164 const auto& sfrom_con_h_then_opstate = con_h_then_opstate.inner_opstate;
165 static_assert(stdexec::__is_instance_of<
166 std::remove_cvref_t<decltype(sfrom_con_h_then_opstate)>,
168 >);
169 using sfrom_con_h_then_rcvr_t = decltype(sfrom_con_h_then_opstate.inner_opstate.completion_signal.rcvr);
170 static_assert(
171 !stdexec::__queryable_with<stdexec::env_of_t<sfrom_con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
172
173 const auto& then_sfrom_con_h_then_opstate = sfrom_con_h_then_opstate.inner_opstate;
174 static_assert(stdexec::__is_instance_of<
175 std::remove_cvref_t<decltype(then_sfrom_con_h_then_opstate)>,
177 >);
178 using then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_con_h_then_opstate.inner_opstate.rcvr);
179 static_assert(!stdexec::__queryable_with<
180 stdexec::env_of_t<then_sfrom_con_h_then_rcvr_t>,
182 >);
183
184 const auto& con_B_then_sfrom_con_h_then_opstate = then_sfrom_con_h_then_opstate.inner_opstate;
185 static_assert(stdexec::__is_instance_of<
186 std::remove_cvref_t<decltype(con_B_then_sfrom_con_h_then_opstate)>,
188 >);
189 using con_B_then_sfrom_con_h_then_rcvr_t = decltype(con_B_then_sfrom_con_h_then_opstate.inner_opstate.rcvr);
190 static_assert(stdexec::__queryable_with<
191 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
193 >);
194 static_assert(std::same_as<
195 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
196 stdexec::env<
197 stdexec::prop<
200 >,
201 stdexec::__env::__fwd<stdexec::env<
202 stdexec::prop<
205 >,
206 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
207 >>
208 >
209 >);
210 ASSERT_EQ(
211 Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_B_then_sfrom_con_h_then_opstate.inner_opstate.rcvr))
212 .get(),
213 exec_B);
214
215 const auto& sfrom_con_B_then_sfrom_con_h_then_opstate = con_B_then_sfrom_con_h_then_opstate.inner_opstate;
216 static_assert(stdexec::__is_instance_of<
217 std::remove_cvref_t<decltype(sfrom_con_B_then_sfrom_con_h_then_opstate)>,
219 >);
220 using sfrom_con_B_then_sfrom_con_h_then_rcvr_t = decltype(sfrom_con_B_then_sfrom_con_h_then_opstate.inner_opstate
221 .completion_signal.rcvr);
222 static_assert(!stdexec::__queryable_with<
223 stdexec::env_of_t<sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
225 >);
226
227 const auto& then_sfrom_B_then_sfrom_con_h_then_opstate = sfrom_con_B_then_sfrom_con_h_then_opstate.inner_opstate;
228 static_assert(stdexec::__is_instance_of<
229 std::remove_cvref_t<decltype(then_sfrom_B_then_sfrom_con_h_then_opstate)>,
231 >);
232 using then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_B_then_sfrom_con_h_then_opstate
233 .inner_opstate.rcvr);
234 static_assert(!stdexec::__queryable_with<
235 stdexec::env_of_t<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
237 >);
238}
239
241TEST_F(ContinuesOnTest, then_sync_wait) {
242 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
243
244 const context_t esc{exec};
245
246 stdexec::sender auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
247
248 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
249
250 ASSERT_THAT(
252 ::testing::ElementsAre(
255
256 ASSERT_EQ(data(), 1);
257}
258
265TEST_F(ContinuesOnTest, transition_to_same_execution_space_instance) {
266 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
267
268 const context_t esc{exec};
269
270 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
271 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
272 | 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(
283
284 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
285}
286
291TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_same_type) {
292 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
293
294 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
295
296 const context_t esc_A{exec_A};
297 Tests::Utils::show_exec_space_id(exec_A, "exec_A");
298 const context_t esc_B{exec_B};
299 Tests::Utils::show_exec_space_id(exec_B, "exec_B");
300
301 auto chain = stdexec::just() | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data)
302 | stdexec::continues_on(esc_B.get_scheduler()) | THEN_INCREMENT(data)
303 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
304
305 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
306
307 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
308
310 ASSERT_THAT(
311 recorded_events,
312 ::testing::ElementsAre(
315 MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(1)),
318 MATCHER_FOR_WAIT_EXEC_EVENT(exec_A, recorded_events.at(4)),
320 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
321 } else {
322 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
323 ASSERT_THAT(
324 recorded_events,
325 ::testing::ElementsAre(
329 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
330 } else {
331 ASSERT_THAT(
332 recorded_events,
333 ::testing::ElementsAre(
335 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "dependency")),
337 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec, "dependency")),
339 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
340 }
341 }
342
343 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
344}
345
350TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_different_type) {
351 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
352
353 const host_execution_space exec_h{};
354
356 const context_t esc{exec};
357
359 Tests::Utils::show_exec_space_id(exec_h, "exec_h");
360
361 using level_C_env_t = Kokkos::Execution::Impl::SyncWait::env;
362 using level_B_env_t = stdexec::__env::__fwd<stdexec::env<
363 stdexec::prop<
366 >,
367 stdexec::__env::__fwd<level_C_env_t>
368 >>;
369 using level_A_env_t = stdexec::__env::__fwd<stdexec::env<
370 stdexec::prop<
373 >,
374 level_B_env_t
375 >>;
376 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
377 | Tests::Utils::check_rcvr_env<level_A_env_t>() | stdexec::continues_on(esc_h.get_scheduler())
379 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
381
382 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
383
384 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
385
387 ASSERT_THAT(
388 recorded_events,
389 ::testing::ElementsAre(
391 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
394 } else if constexpr (
396 && std::same_as<host_execution_space, TEST_EXECUTION_SPACE>) {
397 ASSERT_THAT(
398 recorded_events,
399 ::testing::ElementsAre(
402 MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(1)),
403 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
405 MATCHER_FOR_WAIT_EXEC_EVENT(exec, recorded_events.at(4)),
408 } else {
409 ASSERT_THAT(
410 recorded_events,
411 ::testing::ElementsAre(
414 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
415 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "dependency")),
418 }
419
420 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
421}
422
424consteval bool test_sndr_nothrow_transformable() {
425 using continues_on_sndr_t =
426 decltype(stdexec::just() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()));
427
428 static_assert(std::same_as<
429 stdexec::__demangle_t<continues_on_sndr_t>,
431 stdexec::continues_on_t,
434 stdexec::schedule_from_t,
435 stdexec::__,
437 >
438 >
439 >);
440
441 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
443 stdexec::set_value_t,
444 continues_on_sndr_t&&,
445 stdexec::env<>
446 >);
447
448 using schedule_from_sndr_t = decltype(stdexec::schedule_from(
449 stdexec::schedule(std::declval<typename ContinuesOnTest::scheduler_t>())));
450
451 static_assert(std::same_as<
452 stdexec::__demangle_t<schedule_from_sndr_t>,
454 stdexec::schedule_from_t,
455 stdexec::__,
457 >
458 >);
459
460 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
462 stdexec::set_value_t,
463 schedule_from_sndr_t&&,
464 stdexec::env<>
465 >);
466
467 return true;
468}
469static_assert(test_sndr_nothrow_transformable());
470
471} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_WAIT_EXEC_EVENT(_exec_, _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
Determine if the Kokkos backend can enqueue a wait for an event into an execution space instance.
Kokkos::DefaultHostExecutionSpace host_execution_space
#define THEN_INCREMENT(_data_)
Add a then using Tests::Utils::Functors::Increment that may throw. // NOLINTNEXTLINE(cppcoreguideline...
Definition increment.hpp:59
#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:19
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 >
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:47
Receiver for an object parent_op that implements complete.
Definition receiver.hpp:13
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:54
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:75
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