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}
58
59static_assert(test_schedule_from_sndr_traits());
60
67
68static_assert(test_continues_on_sndr_traits());
69
71TEST_F(ContinuesOnTest, queryable_get_exec) {
72 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
73
74 const host_execution_space exec_h{};
75
77 const context_t esc_A{exec_A}, esc_B{exec_B};
78
79 auto schs_A = stdexec::schedule(esc_A.get_scheduler());
80
82 static_assert(!stdexec::__queryable_with<decltype(stdexec::get_env(schs_A)), Kokkos::Execution::Impl::get_exec_t>);
83
84 auto schs_A_then = std::move(schs_A) | THEN_LABELED('A'); // NOLINT(performance-move-const-arg)
85
86 const auto sch_B = esc_B.get_scheduler();
87 auto schs_A_then_con_B = std::move(schs_A_then) // NOLINT(performance-move-const-arg)
88 | stdexec::continues_on(sch_B);
89
91 ASSERT_EQ(stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B)), sch_B);
92
93 auto schs_A_then_con_B_then = std::move(schs_A_then_con_B) // NOLINT(performance-move-const-arg)
94 | THEN_LABELED('B');
95
96 static_assert(std::same_as<
97 decltype(schs_A_then_con_B_then),
98 stdexec::then_t::__sender<
100 stdexec::__sexpr<
101 stdexec::continues_on_t,
103 stdexec::__sexpr<
104 stdexec::schedule_from_t,
105 stdexec::__,
106 stdexec::then_t::__sender<Tests::Utils::Functors::Labeled<'A'>, schedule_sender_t>
107 >
108 >
109 >
110 >);
111
113 const auto sch_h = esc_h.get_scheduler();
114 auto schs_A_then_con_B_then_con_h_then = std::move(schs_A_then_con_B_then) // NOLINT(performance-move-const-arg)
115 | stdexec::continues_on(sch_h) | THEN_LABELED('h');
116 ASSERT_EQ(
117 stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B_then_con_h_then)),
118 sch_h);
119
120 const auto op_state = stdexec::connect(
121 std::move(schs_A_then_con_B_then_con_h_then), // NOLINT(performance-move-const-arg)
123 .state = std::addressof(esc_h.m_state), .runloop_state = nullptr, .result = nullptr});
124
129 const auto& then_op_state = op_state;
134 std::string_view,
136 >
137 >>;
138 static_assert(std::same_as<decltype(then_op_state.inner_op_state.rcvr), then_rcvr_t>);
139 static_assert(!stdexec::__queryable_with<stdexec::env_of_t<then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
140
142 const auto& con_h_then_op_state = then_op_state.inner_op_state;
143 static_assert(stdexec::__is_instance_of<
144 std::remove_cvref_t<decltype(con_h_then_op_state)>,
146 >);
147 using con_h_then_rcvr_t = decltype(con_h_then_op_state.inner_op_state.rcvr);
148 static_assert(stdexec::__queryable_with<stdexec::env_of_t<con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
149 static_assert(std::same_as<
150 stdexec::env_of_t<con_h_then_rcvr_t>,
151 stdexec::env<
152 stdexec::prop<
155 >,
156 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
157 >
158 >);
159 ASSERT_EQ(
160 Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_h_then_op_state.inner_op_state.rcvr)).get(), exec_h);
161
162 const auto& sfrom_con_h_then_op_state = con_h_then_op_state.inner_op_state;
163 static_assert(stdexec::__is_instance_of<
164 std::remove_cvref_t<decltype(sfrom_con_h_then_op_state)>,
166 >);
167 using sfrom_con_h_then_rcvr_t = decltype(sfrom_con_h_then_op_state.inner_op_state.completion_signal.rcvr);
168 static_assert(
169 !stdexec::__queryable_with<stdexec::env_of_t<sfrom_con_h_then_rcvr_t>, Kokkos::Execution::Impl::get_exec_t>);
170
171 const auto& then_sfrom_con_h_then_op_state = sfrom_con_h_then_op_state.inner_op_state;
172 static_assert(stdexec::__is_instance_of<
173 std::remove_cvref_t<decltype(then_sfrom_con_h_then_op_state)>,
175 >);
176 using then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_con_h_then_op_state.inner_op_state.rcvr);
177 static_assert(!stdexec::__queryable_with<
178 stdexec::env_of_t<then_sfrom_con_h_then_rcvr_t>,
180 >);
181
182 const auto& con_B_then_sfrom_con_h_then_op_state = then_sfrom_con_h_then_op_state.inner_op_state;
183 static_assert(stdexec::__is_instance_of<
184 std::remove_cvref_t<decltype(con_B_then_sfrom_con_h_then_op_state)>,
186 >);
187 using con_B_then_sfrom_con_h_then_rcvr_t = decltype(con_B_then_sfrom_con_h_then_op_state.inner_op_state.rcvr);
188 static_assert(stdexec::__queryable_with<
189 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
191 >);
192 static_assert(std::same_as<
193 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
194 stdexec::env<
195 stdexec::prop<
198 >,
199 stdexec::__env::__fwd<stdexec::env<
200 stdexec::prop<
203 >,
204 stdexec::__env::__fwd<Kokkos::Execution::Impl::SyncWait::env>
205 >>
206 >
207 >);
208 ASSERT_EQ(
209 Kokkos::Execution::Impl::get_exec(stdexec::get_env(con_B_then_sfrom_con_h_then_op_state.inner_op_state.rcvr))
210 .get(),
211 exec_B);
212
213 const auto& sfrom_con_B_then_sfrom_con_h_then_op_state = con_B_then_sfrom_con_h_then_op_state.inner_op_state;
214 static_assert(stdexec::__is_instance_of<
215 std::remove_cvref_t<decltype(sfrom_con_B_then_sfrom_con_h_then_op_state)>,
217 >);
218 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
219 .completion_signal.rcvr);
220 static_assert(!stdexec::__queryable_with<
221 stdexec::env_of_t<sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
223 >);
224
225 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;
226 static_assert(stdexec::__is_instance_of<
227 std::remove_cvref_t<decltype(then_sfrom_B_then_sfrom_con_h_then_op_state)>,
229 >);
230 using then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t = decltype(then_sfrom_B_then_sfrom_con_h_then_op_state
231 .inner_op_state.rcvr);
232 static_assert(!stdexec::__queryable_with<
233 stdexec::env_of_t<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
235 >);
236}
237
239TEST_F(ContinuesOnTest, then_sync_wait) {
240 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
241
242 const context_t esc{exec};
243
244 stdexec::sender auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
245
246 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
247
248 ASSERT_THAT(
250 ::testing::ElementsAre(
253
254 ASSERT_EQ(data(), 1);
255}
256
263TEST_F(ContinuesOnTest, transition_to_same_execution_space_instance) {
264 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
265
266 const context_t esc{exec};
267
268 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
269 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
270 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
271
272 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
273
274 ASSERT_THAT(
276 ::testing::ElementsAre(
281
282 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
283}
284
289TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_same_type) {
290 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
291
292 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
293
294 const context_t esc_A{exec_A};
295 Tests::Utils::show_exec_space_id(exec_A, "exec_A");
296 const context_t esc_B{exec_B};
297 Tests::Utils::show_exec_space_id(exec_B, "exec_B");
298
299 auto chain = stdexec::just() | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data)
300 | stdexec::continues_on(esc_B.get_scheduler()) | THEN_INCREMENT(data)
301 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
302
303 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
304
305 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
306
308 ASSERT_THAT(
309 recorded_events,
310 ::testing::ElementsAre(
313 MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(1)),
316 MATCHER_FOR_WAIT_EXEC_EVENT(exec_A, recorded_events.at(4)),
318 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
319 } else {
320 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
321 ASSERT_THAT(
322 recorded_events,
323 ::testing::ElementsAre(
327 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
328 } else {
329 ASSERT_THAT(
330 recorded_events,
331 ::testing::ElementsAre(
333 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "dependency")),
335 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec, "dependency")),
337 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
338 }
339 }
340
341 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
342}
343
348TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_different_type) {
349 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
350
351 const host_execution_space exec_h{};
352
354 const context_t esc{exec};
355
357 Tests::Utils::show_exec_space_id(exec_h, "exec_h");
358
359 using level_C_env_t = Kokkos::Execution::Impl::SyncWait::env;
360 using level_B_env_t = stdexec::__env::__fwd<stdexec::env<
361 stdexec::prop<
364 >,
365 stdexec::__env::__fwd<level_C_env_t>
366 >>;
367 using level_A_env_t = stdexec::__env::__fwd<stdexec::env<
368 stdexec::prop<
371 >,
372 level_B_env_t
373 >>;
374 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
375 | Tests::Utils::check_rcvr_env<level_A_env_t>() | stdexec::continues_on(esc_h.get_scheduler())
377 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
379
380 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
381
382 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(chain));
383
385 ASSERT_THAT(
386 recorded_events,
387 ::testing::ElementsAre(
389 MATCHER_FOR_BEGIN_SINGLE(exec_h, dispatch_label(exec_h, "then")),
392 } else if constexpr (
394 && std::same_as<host_execution_space, TEST_EXECUTION_SPACE>) {
395 ASSERT_THAT(
396 recorded_events,
397 ::testing::ElementsAre(
400 MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(1)),
401 MATCHER_FOR_BEGIN_SINGLE(exec_h, dispatch_label(exec_h, "then")),
403 MATCHER_FOR_WAIT_EXEC_EVENT(exec, recorded_events.at(4)),
406 } else {
407 ASSERT_THAT(
408 recorded_events,
409 ::testing::ElementsAre(
412 MATCHER_FOR_BEGIN_SINGLE(exec_h, dispatch_label(exec_h, "then")),
413 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "dependency")),
416 }
417
418 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
419}
420
422consteval bool test_sndr_nothrow_transformable() {
423 using continues_on_sndr_t =
424 decltype(stdexec::just() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()));
425
426 static_assert(std::same_as<
427 continues_on_sndr_t,
428 stdexec::__sexpr<
429 stdexec::continues_on_t,
431 stdexec::__sexpr<stdexec::schedule_from_t, stdexec::__, stdexec::just_t::__sender<>>
432 >
433 >);
434
435 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
437 stdexec::set_value_t,
438 continues_on_sndr_t&&,
439 stdexec::env<>
440 >);
441
442 using schedule_from_sndr_t = decltype(stdexec::schedule_from(
443 stdexec::schedule(std::declval<typename ContinuesOnTest::scheduler_t>())));
444
445 static_assert(std::same_as<
446 schedule_from_sndr_t,
447 stdexec::__sexpr<stdexec::schedule_from_t, stdexec::__, typename ContinuesOnTest::schedule_sender_t>
448 >);
449
450 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
452 stdexec::set_value_t,
453 schedule_from_sndr_t&&,
454 stdexec::env<>
455 >);
456
457 return true;
458}
459
460static_assert(test_sndr_nothrow_transformable());
461
462} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, const std::string_view label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_BEGIN_SINGLE(_exec_, _label_)
#define MATCHER_FOR_WAIT_EXEC_EVENT(_exec_, _record_event_variant_)
#define MATCHER_FOR_RECORD_EVENT(_exec_)
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, BeginSingleEvent, 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:64
#define THEN_LABELED(_id_)
Add a then using Tests::Utils::Functors::Labeled. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage).
Definition labeled.hpp:23
constexpr get_exec_t get_exec
Definition get_exec.hpp:24
consteval bool test_schedule_from_sndr_traits()
consteval bool test_sndr_nothrow_transformable()
Definition test_bulk.cpp:87
consteval bool test_continues_on_sndr_traits()
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:38
constexpr check_rcvr_env_t< ExpectedEnv > check_rcvr_env
bool are_same_instances(const Exec &exec, const OtherExec &other_exec)
Definition kokkos.hpp:18
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< execution_space >
Wrap a Kokkos execution space to make it cheap to copy/move in new environments.
Definition get_exec.hpp:52
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:59
Receiver for stdexec::sync_wait.
Definition sync_wait.hpp:45
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:80
decltype(std::declval< const context_t >().get_scheduler()) scheduler_t
Definition context.hpp:34
decltype(stdexec::schedule(std::declval< scheduler_t >())) schedule_sender_t
Definition context.hpp:35
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:33