kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_parallel_for.cpp
Go to the documentation of this file.
1#include <bit>
2
5
7
20
31
33
34using namespace Kokkos::utils::callbacks;
35
49
54template <
55 template <typename...> typename SndrAdptr,
56 bool ExptIsDispatchingSender,
57 template <typename> typename ExptTransformSenderType
58>
59consteval bool test_sndr_traits() {
61 using schd_sndr_t = typename TEST_CATEGORY(ParallelForTest)::schedule_sender_t;
62
64 using label_t = std::string;
65 using functor_t = Tests::Utils::Functors::SumIndices<typename TEST_CATEGORY(ParallelForTest)::view_s_t>;
66 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
67 using pfor_data_t =
69 using pfor_sndr_t = SndrAdptr<schd_sndr_t, TEST_EXECUTION_SPACE, label_t, functor_t, policy_t>;
70
73 static_assert(std::same_as<Kokkos::Execution::Impl::exec_of_t<pfor_sndr_t>, TEST_EXECUTION_SPACE>);
74
76 static_assert(Kokkos::Execution::Impl::dispatching_sender<pfor_sndr_t> == ExptIsDispatchingSender);
77
79 static_assert(!stdexec::dependent_sender<pfor_sndr_t>);
80 static_assert(
81 stdexec::get_completion_signatures<pfor_sndr_t>()
82 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
83
85 static_assert(std::same_as<
86 stdexec::__completion_domain_of_t<stdexec::set_value_t, pfor_sndr_t, stdexec::env<>>,
88 >);
89
91 static_assert(std::same_as<
94 >);
95
97 static_assert(stdexec::sender_to<pfor_sndr_t, Tests::Utils::SinkReceiver>);
98
99 static_assert(std::same_as<
100 stdexec::transform_sender_result_t<pfor_sndr_t, stdexec::env_of_t<Tests::Utils::SinkReceiver>>,
101 typename ExptTransformSenderType<Kokkos::Execution::ExecutionSpaceImpl::ParallelForSender< // NOLINT(modernize-type-traits)
103 schd_sndr_t,
104 TEST_EXECUTION_SPACE,
105 pfor_data_t
106 >>::type
107 >);
108
110 static_assert(stdexec::__nothrow_connectable<pfor_sndr_t, Tests::Utils::SinkReceiver>);
111
112 return true;
113}
114
115template <typename Sndr, typename Exec, typename Label, typename Functor, typename Policy>
117 Sndr,
119>;
120
121template <typename Sndr, typename Exec, typename Label, typename Functor, typename Policy>
124 Sndr,
125 Exec,
127>;
128
131
135 using schd_sndr_t = typename TEST_CATEGORY(ParallelForTest)::schedule_sender_t;
136
139 using functor_t = Tests::Utils::Functors::SumIndices<typename TEST_CATEGORY(ParallelForTest)::view_s_t>;
140 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
143
145 static_assert(stdexec::__applicable<
147 pfor_sndr_t,
148 const stdexec::env<>&
149 >);
150
151 return true;
152}
153
154static_assert(test_sndr_is_transformable());
155
157template <typename IterSpace>
160 using schd_sndr_t = typename TEST_CATEGORY(ParallelForTest)::schedule_sender_t;
161
164 using functor_t = Tests::Utils::Functors::SumIndices<typename TEST_CATEGORY(ParallelForTest)::view_s_t>;
167
168 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
170 stdexec::set_value_t,
171 pfor_sndr_t&&,
172 stdexec::env<>
173 >);
174
176 static_assert(
177 std::same_as<
178 decltype(std::declval<typename stdexec::transform_sender_result_t<pfor_sndr_t, stdexec::env<>>::closure_t>()
179 .get_policy()),
180 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>
181 >);
182
183 return true;
184}
185
186static_assert(
187 test_pfor_sndr_with_index_space_transforms_into_pfor_sndr_with_range_policy<Kokkos::range_slice<size_t, size_t>>());
188static_assert(test_pfor_sndr_with_index_space_transforms_into_pfor_sndr_with_range_policy<Kokkos::dims<1>>());
189
191template <typename ViewType>
192consteval bool test_closure_traits() {
194 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
195 using pfor_data_t =
198
201
202 static_assert(std::is_nothrow_move_constructible_v<closure_t>);
203
204 return true;
205}
206
207static_assert(test_closure_traits<typename TEST_CATEGORY(ParallelForTest)::view_s_t>());
208static_assert(test_closure_traits<std::span<int>>());
209
212 typename TEST_CATEGORY(ParallelForTest)::scheduler_t,
213 Kokkos::Execution::parallel_for_t,
214 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>,
215 Tests::Utils::Functors::NoOp<false, false, false>
216>());
217
219TEST_F(TEST_CATEGORY(ParallelForTest), team_policy) {
220 constexpr int size = 32;
221
222 const auto [num_teams, team_size] = [&]() {
224#if defined(KOKKOS_ENABLE_HPX)
225 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Experimental::HPX>) {
226 return std::make_tuple(size, 1);
227 }
228#endif
229 const int team_size_ = std::bit_floor(static_cast<unsigned short>(std::min(exec.concurrency(), size / 2)));
230 return std::make_tuple(size / team_size_, team_size_);
231 }();
232
233 ASSERT_EQ(team_size * num_teams, size);
234
235 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
236
237 const context_t esc{exec};
238
239 auto chain = stdexec::schedule(esc.get_scheduler())
241 "hello from pfor",
242 Kokkos::TeamPolicy<TEST_EXECUTION_SPACE>(num_teams, team_size),
243 Tests::Utils::Functors::SumIndices{.data = witness});
244
245 stdexec::sync_wait(std::move(chain));
246
247 ASSERT_EQ(witness(), size / 2 * (size - 1));
248}
249
250template <typename ViewType, Kokkos::ExecutionSpace Exec>
252 const size_t size,
253 const ViewType& witness,
254 const Kokkos::Execution::ExecutionSpaceContext<Exec>& esc) -> stdexec::sender auto {
255 return stdexec::schedule(esc.get_scheduler())
257 "passing label, execution policy and functor",
258 Kokkos::RangePolicy<Exec>(0, size),
259 Tests::Utils::Functors::SumIndices{.data = witness})
261 Kokkos::RangePolicy<Exec>(0, size), Tests::Utils::Functors::SumIndices{.data = witness})
263 "passing label, work count and functor", size, Tests::Utils::Functors::SumIndices{.data = witness})
266 Kokkos::range_slice<size_t, size_t>{4, 6}, Tests::Utils::Functors::SumIndices{.data = witness});
267}
268
271 constexpr size_t size = 10;
272
273 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
274
275 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(
277
279
280 ASSERT_THAT(
281 recorded_events,
282 testing::ElementsAre(
283 MATCHER_FOR_BEGIN_PFOR(exec, "passing label, execution policy and functor"),
285 MATCHER_FOR_BEGIN_PFOR(exec, "passing label, work count and functor"),
289
290 ASSERT_EQ(witness(), 4 * size / 2 * (size - 1) + (4 + 5));
291}
292
294TEST_F(TEST_CATEGORY(ParallelForTest), two_parallel_regions) {
295 constexpr size_t size = 10;
296
297 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
298
299 const context_t esc{exec};
300
301 auto chain =
302 stdexec::schedule(esc.get_scheduler())
304 std::format("{}: hello from pfor", Kokkos::Execution::Impl::display_string_of<TEST_EXECUTION_SPACE>()),
305 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
306 Tests::Utils::Functors::SumIndices{.data = witness})
307 | stdexec::then(
309 .prev = size / 2 * (size - 1), .value = 4, .data = witness.data()})
311 std::format(
313 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, 2 * size),
314 Tests::Utils::Functors::SumIndices{.data = witness});
315
316 ASSERT_THAT(
318 testing::ElementsAre(
319 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
321 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello again from pfor")),
323
324 ASSERT_EQ(witness(), size / 2 * (size - 1) + 4 + 2 * size * (2 * size - 1) / 2);
325}
326
328TEST_F(TEST_CATEGORY(ParallelForTest), starts_on_parallel_region) {
329 constexpr size_t size = 10;
330
331 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
332
333 auto chain =
334 stdexec::just()
336 std::format("{}: hello from pfor", Kokkos::Execution::Impl::display_string_of<TEST_EXECUTION_SPACE>()),
337 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
338 Tests::Utils::Functors::SumIndices{.data = witness});
339
340 using chain_t = decltype(chain);
341
342 static_assert(
343 stdexec::get_completion_signatures<chain_t>()
344 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
346 static_assert(
347 std::same_as<stdexec::__completion_domain_of_t<stdexec::set_value_t, chain_t>, stdexec::indeterminate_domain<>>);
348
349 const context_t esc{exec};
350 auto starts_on = stdexec::starts_on(esc.get_scheduler(), std::move(chain));
351
352 using starts_on_t = decltype(starts_on);
353
354 static_assert(stdexec::__has_eptr_completion<chain_t>);
355 static_assert(!stdexec::dependent_sender<starts_on_t>);
356 static_assert(
357 stdexec::get_completion_signatures<starts_on_t>()
358 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
359
366 static_assert(stdexec::__is_instance_of<
367 stdexec::transform_sender_result_t<
368 decltype(starts_on),
369 stdexec::env_of_t<Kokkos::Execution::Impl::SyncWait::Receiver<TEST_EXECUTION_SPACE>>
370 >,
371 stdexec::__seq::__sndr
372 >);
373
374 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(starts_on));
375
376 ASSERT_THAT(recorded_events, [&]() {
378 return testing::ElementsAre(
379 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
381 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
382 } else {
383 return testing::ElementsAre(
384 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
385 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
386 }
387 }());
388
389 ASSERT_EQ(witness(), size / 2 * (size - 1));
390}
391
393TEST_F(TEST_CATEGORY(ParallelForTest), forwarding_env) {
394 constexpr size_t size = 10;
395
396 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
397
398 std::atomic<size_t> count = 0;
399
400 int value;
401
402 const context_t esc{exec};
403
404 stdexec::sender auto sndr =
405 stdexec::read_env(stdexec::get_allocator)
406 | stdexec::then([&value](auto allocator) { value = Tests::Utils::round_trip_allocate(allocator, 42); })
407 | stdexec::continues_on(esc.get_scheduler())
410 "my pfor",
411 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
413 | stdexec::write_env(stdexec::prop{stdexec::get_allocator, Tests::Utils::TrackingAllocator<int>{&count}});
414
415 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
416
417 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
418
419 ASSERT_THAT(recorded_events, [&]() {
421 return testing::ElementsAre(
422 MATCHER_FOR_BEGIN_PFOR(exec, "my pfor"),
424 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
425 } else {
426 return testing::ElementsAre(
427 MATCHER_FOR_BEGIN_PFOR(exec, "my pfor"),
428 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
429 }
430 }());
431
432 ASSERT_EQ(data(), size / 2 * (size - 1));
433
434 ASSERT_EQ(value, 42);
435 ASSERT_EQ(count, 1);
436}
437
439TEST_F(TEST_CATEGORY(ParallelForTest), perfect_forwarding_connect) {
440 using counter_t = Tests::Utils::Functors::Counter;
441
442 const context_t esc{exec};
443
444 const Kokkos::RangePolicy<TEST_EXECUTION_SPACE> policy(0, 1);
445 counter_t counter;
446
447 counter_t::reset();
448
449 [[maybe_unused]]
450 const stdexec::operation_state auto opstate_from_lvalue_functor = stdexec::connect(
451 stdexec::schedule(esc.get_scheduler()) | Kokkos::Execution::parallel_for("label", policy, counter),
453
454 ASSERT_EQ(counter_t::copy_assignments, 0);
455 ASSERT_EQ(counter_t::copy_constructions, 1);
456 ASSERT_EQ(counter_t::move_assignments, 0);
457 ASSERT_EQ(counter_t::move_constructions, 6);
458
459 counter_t::reset();
460
461 [[maybe_unused]]
462 const stdexec::operation_state auto closure_from_xvalue_functor = stdexec::connect(
463 stdexec::schedule(esc.get_scheduler()) | Kokkos::Execution::parallel_for("label", policy, std::move(counter)),
465
466 ASSERT_EQ(counter_t::copy_assignments, 0);
467 ASSERT_EQ(counter_t::copy_constructions, 0);
468 ASSERT_EQ(counter_t::move_assignments, 0);
469 ASSERT_EQ(counter_t::move_constructions, 7);
470
471 counter_t::reset();
472
473 [[maybe_unused]]
474 const stdexec::operation_state auto closure_from_prvalue_functor = stdexec::connect(
475 stdexec::schedule(esc.get_scheduler()) | Kokkos::Execution::parallel_for("label", policy, counter_t{}),
477
478 ASSERT_EQ(counter_t::copy_assignments, 0);
479 ASSERT_EQ(counter_t::copy_constructions, 0);
480 ASSERT_EQ(counter_t::move_assignments, 0);
481 ASSERT_EQ(counter_t::move_constructions, 7);
482}
483
484} // 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_EVENT(_record_event_variant_)
#define MATCHER_FOR_BEGIN_PFOR(_exec_, _label_)
#define MATCHER_FOR_RECORD_EVENT(_exec_)
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
#define TEST_CATEGORY(_name_)
Definition category.hpp:10
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, BeginParallelForEvent, BeginSingleEvent, Kokkos::Execution::Impl::RecordEvent, Kokkos::Execution::Impl::WaitEvent > recorder_listener_t
Concept for a sender whose completion scheduler is Kokkos::Execution::ExecutionSpaceImpl::Scheduler.
Concept that constrains the type of a sender that dispatches a functor for execution.
std::invoke_result_t< stdexec::get_completion_scheduler_t< Tag >, stdexec::env_of_t< Sndr >, Env... > completion_scheduler_of_t
Retrieve the completion scheduler for a given completion tag.
constexpr auto display_string_of() noexcept -> std::string_view
Definition type_info.hpp:20
constexpr parallel_for_t parallel_for
Kokkos::Execution::ExecutionSpaceImpl::ParallelForSender< Kokkos::Execution::parallel_for_t, Sndr, Exec, Kokkos::Execution::Impl::ParallelForData< Functor, Policy, Kokkos::Execution::Impl::Label< Label > > > pfor_impl_t
consteval bool test_pfor_sndr_with_index_space_transforms_into_pfor_sndr_with_range_policy()
consteval bool test_sndr_traits()
Definition test_bulk.cpp:49
auto closure_object_creation_overloads(const size_t size, const ViewType &witness, const Kokkos::Execution::ExecutionSpaceContext< Exec > &esc) -> stdexec::sender auto
consteval bool test_sndr_is_transformable()
Kokkos::Execution::Impl::ParallelForSender< Sndr, Kokkos::Execution::Impl::ParallelForData< Functor, Policy, Kokkos::Execution::Impl::Label< Label > > > pfor_generic_t
auto record_sync_wait(Sndr &&sndr)
Definition sync_wait.hpp:14
consteval bool check_continues_on_after_just_stopped()
constexpr check_rcvr_env_queryable_with_t< true, Queries... > check_rcvr_env_queryable_with
auto round_trip_allocate(Allocator &allocator, T &&value)
Tests::Utils::Functors::NoOp< false, false, false > NoOp
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 >
Scheduler for a Kokkos execution space.
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:59
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:80
Custom algorithm for the Kokkos::parallel_for construct.
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:33
Count construction, move/copy construction, move/copy assignment and destruction atomically on host.
Definition counter.hpp:14
Load the value at data and check it is equal to prev. Then, add value to it.
A receiver that can handle all completions and does nothing with them.
A minimal tracking allocator.