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 const 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 const 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_op_state = 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_op_state.inner_op_state.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_op_state = then_op_state.inner_op_state;
146 static_assert(stdexec::__is_instance_of<
147 std::remove_cvref_t<decltype(con_h_then_op_state)>,
149 >);
150 using con_h_then_rcvr_t = decltype(con_h_then_op_state.inner_op_state.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(
163 Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_h_then_op_state.inner_op_state.rcvr)).get(), exec_h);
164
165 const auto& sfrom_con_h_then_op_state = con_h_then_op_state.inner_op_state;
166 static_assert(stdexec::__is_instance_of<
167 std::remove_cvref_t<decltype(sfrom_con_h_then_op_state)>,
169 >);
170 using sfrom_con_h_then_rcvr_t = decltype(sfrom_con_h_then_op_state.inner_op_state.completion_signal.rcvr);
171 static_assert(
172 !stdexec::__queryable_with<stdexec::env_of_t<sfrom_con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
173
174 const auto& then_sfrom_con_h_then_op_state = sfrom_con_h_then_op_state.inner_op_state;
175 static_assert(stdexec::__is_instance_of<
176 std::remove_cvref_t<decltype(then_sfrom_con_h_then_op_state)>,
178 >);
179 using then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_con_h_then_op_state.inner_op_state.rcvr);
180 static_assert(!stdexec::__queryable_with<
181 stdexec::env_of_t<then_sfrom_con_h_then_rcvr_t>,
183 >);
184
185 const auto& con_B_then_sfrom_con_h_then_op_state = then_sfrom_con_h_then_op_state.inner_op_state;
186 static_assert(stdexec::__is_instance_of<
187 std::remove_cvref_t<decltype(con_B_then_sfrom_con_h_then_op_state)>,
189 >);
190 using con_B_then_sfrom_con_h_then_rcvr_t = decltype(con_B_then_sfrom_con_h_then_op_state.inner_op_state.rcvr);
191 static_assert(stdexec::__queryable_with<
192 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
194 >);
195 static_assert(std::same_as<
196 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
197 stdexec::env<
198 stdexec::prop<
201 >,
202 stdexec::__env::__fwd<stdexec::env<
203 stdexec::prop<
206 >,
207 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
208 >>
209 >
210 >);
211 ASSERT_EQ(
212 Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_B_then_sfrom_con_h_then_op_state.inner_op_state.rcvr))
213 .get(),
214 exec_B);
215
216 const auto& sfrom_con_B_then_sfrom_con_h_then_op_state = con_B_then_sfrom_con_h_then_op_state.inner_op_state;
217 static_assert(stdexec::__is_instance_of<
218 std::remove_cvref_t<decltype(sfrom_con_B_then_sfrom_con_h_then_op_state)>,
220 >);
221 using sfrom_con_B_then_sfrom_con_h_then_rcvr_t = decltype(sfrom_con_B_then_sfrom_con_h_then_op_state.inner_op_state
222 .completion_signal.rcvr);
223 static_assert(!stdexec::__queryable_with<
224 stdexec::env_of_t<sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
226 >);
227
228 const auto& then_sfrom_B_then_sfrom_con_h_then_op_state = sfrom_con_B_then_sfrom_con_h_then_op_state.inner_op_state;
229 static_assert(stdexec::__is_instance_of<
230 std::remove_cvref_t<decltype(then_sfrom_B_then_sfrom_con_h_then_op_state)>,
232 >);
233 using then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_B_then_sfrom_con_h_then_op_state
234 .inner_op_state.rcvr);
235 static_assert(!stdexec::__queryable_with<
236 stdexec::env_of_t<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
238 >);
239}
240
242TEST_F(ContinuesOnTest, then_sync_wait) {
243 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
244
245 const context_t esc{exec};
246
247 stdexec::sender auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
248
249 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
250
251 ASSERT_THAT(
253 ::testing::ElementsAre(
256
257 ASSERT_EQ(data(), 1);
258}
259
266TEST_F(ContinuesOnTest, transition_to_same_execution_space_instance) {
267 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
268
269 const context_t esc{exec};
270
271 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
272 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
273 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
274
275 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
276
277 ASSERT_THAT(
279 ::testing::ElementsAre(
284
285 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
286}
287
292TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_same_type) {
293 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
294
295 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
296
297 const context_t esc_A{exec_A};
298 Tests::Utils::show_exec_space_id(exec_A, "exec_A");
299 const context_t esc_B{exec_B};
300 Tests::Utils::show_exec_space_id(exec_B, "exec_B");
301
302 auto chain = stdexec::just() | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data)
303 | stdexec::continues_on(esc_B.get_scheduler()) | THEN_INCREMENT(data)
304 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
305
306 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
307
308 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
309
311 ASSERT_THAT(
312 recorded_events,
313 ::testing::ElementsAre(
316 MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(1)),
319 MATCHER_FOR_WAIT_EXEC_EVENT(exec_A, recorded_events.at(4)),
321 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
322 } else {
323 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
324 ASSERT_THAT(
325 recorded_events,
326 ::testing::ElementsAre(
330 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
331 } else {
332 ASSERT_THAT(
333 recorded_events,
334 ::testing::ElementsAre(
336 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "dependency")),
338 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec, "dependency")),
340 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
341 }
342 }
343
344 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
345}
346
351TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_different_type) {
352 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
353
354 const host_execution_space exec_h{};
355
357 const context_t esc{exec};
358
360 Tests::Utils::show_exec_space_id(exec_h, "exec_h");
361
362 using level_C_env_t = Kokkos::Execution::Impl::SyncWait::env;
363 using level_B_env_t = stdexec::__env::__fwd<stdexec::env<
364 stdexec::prop<
367 >,
368 stdexec::__env::__fwd<level_C_env_t>
369 >>;
370 using level_A_env_t = stdexec::__env::__fwd<stdexec::env<
371 stdexec::prop<
374 >,
375 level_B_env_t
376 >>;
377 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
378 | Tests::Utils::check_rcvr_env<level_A_env_t>() | stdexec::continues_on(esc_h.get_scheduler())
380 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
382
383 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
384
385 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
386
388 ASSERT_THAT(
389 recorded_events,
390 ::testing::ElementsAre(
392 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
395 } else if constexpr (
397 && std::same_as<host_execution_space, TEST_EXECUTION_SPACE>) {
398 ASSERT_THAT(
399 recorded_events,
400 ::testing::ElementsAre(
403 MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(1)),
404 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
406 MATCHER_FOR_WAIT_EXEC_EVENT(exec, recorded_events.at(4)),
409 } else {
410 ASSERT_THAT(
411 recorded_events,
412 ::testing::ElementsAre(
415 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
416 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "dependency")),
419 }
420
421 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
422}
423
425consteval bool test_sndr_nothrow_transformable() {
426 using continues_on_sndr_t =
427 decltype(stdexec::just() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()));
428
429 static_assert(std::same_as<
430 stdexec::__demangle_t<continues_on_sndr_t>,
432 stdexec::continues_on_t,
435 stdexec::schedule_from_t,
436 stdexec::__,
438 >
439 >
440 >);
441
442 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
444 stdexec::set_value_t,
445 continues_on_sndr_t&&,
446 stdexec::env<>
447 >);
448
449 using schedule_from_sndr_t = decltype(stdexec::schedule_from(
450 stdexec::schedule(std::declval<typename ContinuesOnTest::scheduler_t>())));
451
452 static_assert(std::same_as<
453 stdexec::__demangle_t<schedule_from_sndr_t>,
455 stdexec::schedule_from_t,
456 stdexec::__,
458 >
459 >);
460
461 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
463 stdexec::set_value_t,
464 schedule_from_sndr_t&&,
465 stdexec::env<>
466 >);
467
468 return true;
469}
470static_assert(test_sndr_nothrow_transformable());
471
472} // 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