kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_parallel_reduce.cpp
Go to the documentation of this file.
1#include <bit>
2
5
7
20
31
33
34using namespace Kokkos::utils::callbacks;
35
51
56template <
57 template <typename...> typename SndrAdptr,
58 bool ExptIsDispatchingSender,
59 template <typename> typename ExptTransformSenderType
60>
61consteval bool test_sndr_traits() {
63 using schd_sndr_t = typename TEST_CATEGORY(ParallelReduceTest)::schedule_sender_t;
64
66 using label_t = std::string;
68 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
69 using reducer_t = typename TEST_CATEGORY(ParallelReduceTest)::view_s_t;
71 functor_t,
72 policy_t,
73 reducer_t,
75 >;
76 using pred_sndr_t = SndrAdptr<schd_sndr_t, TEST_EXECUTION_SPACE, label_t, functor_t, policy_t, reducer_t>;
77
80 static_assert(std::same_as<Kokkos::Execution::Impl::exec_of_t<pred_sndr_t>, TEST_EXECUTION_SPACE>);
81
83 static_assert(Kokkos::Execution::Impl::dispatching_sender<pred_sndr_t> == ExptIsDispatchingSender);
84
86 static_assert(!stdexec::dependent_sender<pred_sndr_t>);
87 static_assert(
88 stdexec::get_completion_signatures<pred_sndr_t>()
89 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
90
92 static_assert(std::same_as<
93 stdexec::__completion_domain_of_t<stdexec::set_value_t, pred_sndr_t, stdexec::env<>>,
95 >);
96
98 static_assert(std::same_as<
101 >);
102
104 static_assert(stdexec::sender_to<pred_sndr_t, Tests::Utils::SinkReceiver>);
105
106 static_assert(std::same_as<
107 stdexec::transform_sender_result_t<pred_sndr_t, stdexec::env_of_t<Tests::Utils::SinkReceiver>>,
108 typename ExptTransformSenderType<Kokkos::Execution::ExecutionSpaceImpl::ParallelReduceSender< // NOLINT(modernize-type-traits)
110 schd_sndr_t,
111 TEST_EXECUTION_SPACE,
112 pred_data_t
113 >>::type
114 >);
115
117 static_assert(stdexec::__nothrow_connectable<pred_sndr_t, Tests::Utils::SinkReceiver>);
118
119 return true;
120}
121
122template <typename Sndr, typename Exec, typename Label, typename Functor, typename Policy, typename Reducer>
124 Sndr,
126>;
127
128template <typename Sndr, typename Exec, typename Label, typename Functor, typename Policy, typename Reducer>
131 Sndr,
132 Exec,
134>;
135
138
140consteval bool test_sndr_is_transformable() {
142 using schd_sndr_t = typename TEST_CATEGORY(ParallelReduceTest)::schedule_sender_t;
143
145 using label_t = std::string;
147 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
148 using reducer_t = typename TEST_CATEGORY(ParallelReduceTest)::view_s_t;
151
153 static_assert(stdexec::__applicable<
155 pred_sndr_t,
156 const stdexec::env<>&
157 >);
158
159 return true;
160}
161
162static_assert(test_sndr_is_transformable());
163
165template <typename ViewType>
166consteval bool test_closure_traits() {
168 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
169 using reducer_t = ViewType;
171 functor_t,
172 policy_t,
173 reducer_t,
175 >;
177
180
181 static_assert(std::is_nothrow_move_constructible_v<closure_t>);
182
183 return true;
184}
185
186static_assert(test_closure_traits<typename TEST_CATEGORY(ParallelReduceTest)::view_s_t>());
187static_assert(test_closure_traits<std::span<int>>());
188
191 typename TEST_CATEGORY(ParallelReduceTest)::scheduler_t,
192 Kokkos::Execution::parallel_reduce_t,
193 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>,
194 Tests::Utils::Functors::NoOp<false, false, false>,
195 typename TEST_CATEGORY(ParallelReduceTest)::view_s_t
196>());
197
198template <typename ViewType>
199struct ParallelReduceManagedAndUnManagedTest : public TEST_CATEGORY(ParallelReduceTest) { };
200
202
205 const typename TestFixture::view_s_t target_storage(Kokkos::view_alloc(this->exec, "data - shared space"));
206
207 const TypeParam target(target_storage); // NOLINT(performance-unnecessary-copy-initialization)
208
209 const typename TestFixture::context_t esc{this->exec};
210
211 constexpr size_t size = 10;
212
213 ASSERT_THAT(
215 stdexec::schedule(esc.get_scheduler())
217 "hello from pred",
218 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
220 target)),
221 testing::ElementsAre(
222 MATCHER_FOR_BEGIN_PRED(this->exec, "hello from pred"),
223 MATCHER_FOR_BEGIN_FENCE(this->exec, dispatch_label(this->exec, "sync_wait"))));
224
225 ASSERT_EQ(target(), size / 2 * (size - 1));
226}
227
229
230using pred_managed_and_unmanaged_types_t = ::testing::Types<
231 typename TEST_CATEGORY(ParallelReduceTest)::view_s_t,
232 typename TEST_CATEGORY(ParallelReduceTest)::view_um_s_t
233>;
234
235INSTANTIATE_TYPED_TEST_SUITE_P(
236 ParallelReduceManagedAndUnManagedTestInstantiation,
239
241TEST_F(TEST_CATEGORY(ParallelReduceTest), range_policy_maxloc_reducer) {
242 constexpr size_t size = 5;
243
244 using data_view_t = Kokkos::View<value_t[size], TEST_EXECUTION_SPACE>;
245 using max_loc_t = Kokkos::MaxLoc<value_t, typename TEST_EXECUTION_SPACE::index_type, Kokkos::SharedSpace>;
246 using max_loc_functor_t = Tests::Utils::Functors::MaxLoc<data_view_t>;
247
248 const data_view_t data(Kokkos::view_alloc(Kokkos::WithoutInitializing, exec));
249
250 const context_t esc{exec};
251
252 const typename max_loc_t::result_view_type target(Kokkos::view_alloc(exec));
253
254 using combined_functor_reducer_t = Kokkos::Impl::CombinedFunctorReducer<
255 max_loc_functor_t,
256 Kokkos::Impl::FunctorAnalysis<
257 Kokkos::Impl::FunctorPatternInterface::REDUCE,
258 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>,
259 max_loc_t,
260 typename max_loc_t::value_type
261 >::Reducer,
262 void
263 >;
264
265 ASSERT_THAT(
267 stdexec::schedule(esc.get_scheduler())
269 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, 1),
270 KOKKOS_LAMBDA<std::integral T>(const T) {
271 data(0) = 42;
272 data(1) = -5;
273 data(2) = 6;
274 data(3) = 56;
275 data(4) = -6;
276 })
278 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
279 max_loc_functor_t{.data = data},
280 max_loc_t{target})),
281 testing::ElementsAre(
283 MATCHER_FOR_BEGIN_PRED(exec, Kokkos::Impl::TypeInfo<combined_functor_reducer_t>::name()),
285
286 ASSERT_EQ(target().val, 56);
287 ASSERT_EQ(target().loc, 3);
288}
289
291TEST_F(TEST_CATEGORY(ParallelReduceTest), team_policy) {
292 constexpr int size = 2048;
293
294 const auto [num_teams, team_size] = [&]() {
296#if defined(KOKKOS_ENABLE_HPX)
297 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Experimental::HPX>) {
298 return std::make_tuple(size, 1);
299 }
300#endif
301 const int team_size_ = std::bit_floor(static_cast<unsigned short>(std::min(exec.concurrency(), size / 2)));
302 return std::make_tuple(size / team_size_, team_size_);
303 }();
304
305 ASSERT_EQ(team_size * num_teams, size);
306
307 const view_s_t target(Kokkos::view_alloc(exec, "data - shared space"));
308
309 const context_t esc{exec};
310
311 stdexec::sync_wait(
312 stdexec::schedule(esc.get_scheduler())
314 Kokkos::TeamPolicy<TEST_EXECUTION_SPACE>(num_teams, team_size),
316 TEST_EXECUTION_SPACE,
318 >{},
319 target));
320
321 ASSERT_EQ(target(), size / 2 * (size - 1));
322}
323
324template <typename ViewType, Kokkos::ExecutionSpace Exec>
326 const size_t size,
327 const ViewType& target,
328 const Kokkos::Execution::ExecutionSpaceContext<Exec>& esc) -> stdexec::sender auto {
329 return stdexec::schedule(esc.get_scheduler())
331 "passing label, execution policy, functor and target",
332 Kokkos::RangePolicy<Exec>(0, size),
334 target)
336 Kokkos::RangePolicy<Exec>(0, size), Tests::Utils::Functors::ReduceIndices{}, target)
338 "passing label, work count, functor and target", size, Tests::Utils::Functors::ReduceIndices{}, target)
339 | Kokkos::Execution::parallel_reduce(size, Tests::Utils::Functors::ReduceIndices{}, target)
341 Kokkos::range_slice<size_t, size_t>{4, 6}, Tests::Utils::Functors::ReduceIndices{}, target);
342}
343
346 constexpr size_t size = 10;
347
348 const view_s_t target(Kokkos::view_alloc(exec, "data - shared space"));
349
350 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(
352
354 using combined_functor_reducer_t = Kokkos::Impl::CombinedFunctorReducer<
355 functor_t,
356 Kokkos::Impl::FunctorAnalysis<
357 Kokkos::Impl::FunctorPatternInterface::REDUCE,
358 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>,
359 functor_t,
360 int
361 >::Reducer,
362 void
363 >;
364
365 ASSERT_THAT(
366 recorded_events,
367 testing::ElementsAre(
368 MATCHER_FOR_BEGIN_PRED(exec, "passing label, execution policy, functor and target"),
369 MATCHER_FOR_BEGIN_PRED(exec, Kokkos::Impl::TypeInfo<combined_functor_reducer_t>::name()),
370 MATCHER_FOR_BEGIN_PRED(exec, "passing label, work count, functor and target"),
371 MATCHER_FOR_BEGIN_PRED(exec, Kokkos::Impl::TypeInfo<combined_functor_reducer_t>::name()),
372 MATCHER_FOR_BEGIN_PRED(exec, Kokkos::Impl::TypeInfo<combined_functor_reducer_t>::name()),
374
375 ASSERT_EQ(target(), 4 + 5);
376}
377
379TEST_F(TEST_CATEGORY(ParallelReduceTest), two_parallel_regions) {
380 constexpr size_t size = 10;
381
382 const view_s_t target(Kokkos::view_alloc(exec, "data - shared space"));
383
384 const context_t esc{exec};
385
386 auto chain =
387 stdexec::schedule(esc.get_scheduler())
389 std::format("{}: hello from pred", Kokkos::Execution::Impl::display_string_of<TEST_EXECUTION_SPACE>()),
390 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
392 target)
393 | stdexec::then(
395 .prev = size / 2 * (size - 1), .value = 0, .data = target.data()})
397 std::format(
399 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, 2 * size),
401 target);
402
403 ASSERT_THAT(
405 testing::ElementsAre(
406 MATCHER_FOR_BEGIN_PRED(exec, dispatch_label(exec, "hello from pred")),
408 MATCHER_FOR_BEGIN_PRED(exec, dispatch_label(exec, "hello again from pred")),
410
411 ASSERT_EQ(target(), 2 * size * (2 * size - 1) / 2);
412}
413
415TEST_F(TEST_CATEGORY(ParallelReduceTest), starts_on_parallel_region) {
416 constexpr size_t size = 10;
417
418 const view_s_t target(Kokkos::view_alloc(exec, "data - shared space"));
419
420 auto chain =
421 stdexec::just()
423 std::format("{}: hello from pred", Kokkos::Execution::Impl::display_string_of<TEST_EXECUTION_SPACE>()),
424 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
426 target);
427
428 using chain_t = decltype(chain);
429
430 static_assert(
431 stdexec::get_completion_signatures<chain_t>()
432 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
434 static_assert(
435 std::same_as<stdexec::__completion_domain_of_t<stdexec::set_value_t, chain_t>, stdexec::indeterminate_domain<>>);
436
437 const context_t esc{exec};
438 auto starts_on = stdexec::starts_on(esc.get_scheduler(), std::move(chain));
439
440 using starts_on_t = decltype(starts_on);
441
442 static_assert(stdexec::__has_eptr_completion<chain_t>);
443 static_assert(!stdexec::dependent_sender<starts_on_t>);
444 static_assert(
445 stdexec::get_completion_signatures<starts_on_t>()
446 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
447
454 static_assert(stdexec::__is_instance_of<
455 stdexec::transform_sender_result_t<
456 decltype(starts_on),
457 stdexec::env_of_t<Kokkos::Execution::Impl::SyncWait::Receiver<TEST_EXECUTION_SPACE>>
458 >,
459 stdexec::__seq::__sndr
460 >);
461
462 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(starts_on));
463
464 ASSERT_THAT(recorded_events, [&]() {
466 return testing::ElementsAre(
467 MATCHER_FOR_BEGIN_PRED(exec, dispatch_label(exec, "hello from pred")),
469 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
470 } else {
471 return testing::ElementsAre(
472 MATCHER_FOR_BEGIN_PRED(exec, dispatch_label(exec, "hello from pred")),
473 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
474 }
475 }());
476
477 ASSERT_EQ(target(), size / 2 * (size - 1));
478}
479
481TEST_F(TEST_CATEGORY(ParallelReduceTest), forwarding_env) {
482 constexpr size_t size = 10;
483
484 const view_s_t target(Kokkos::view_alloc(exec, "data - shared space"));
485
486 std::atomic<size_t> count = 0;
487
488 int value;
489
490 const context_t esc{exec};
491
492 stdexec::sender auto sndr =
493 stdexec::read_env(stdexec::get_allocator)
494 | stdexec::then([&value](auto allocator) { value = Tests::Utils::round_trip_allocate(allocator, 42); })
495 | stdexec::continues_on(esc.get_scheduler())
498 "my pred",
499 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
501 target)
502 | stdexec::write_env(stdexec::prop{stdexec::get_allocator, Tests::Utils::TrackingAllocator<int>{&count}});
503
504 ASSERT_EQ(target(), 0) << "Eager execution is not allowed.";
505
506 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(sndr));
507
508 ASSERT_THAT(recorded_events, [&]() {
510 return testing::ElementsAre(
511 MATCHER_FOR_BEGIN_PRED(exec, "my pred"),
513 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
514 } else {
515 return testing::ElementsAre(
516 MATCHER_FOR_BEGIN_PRED(exec, "my pred"),
517 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
518 }
519 }());
520
521 ASSERT_EQ(target(), size / 2 * (size - 1));
522
523 ASSERT_EQ(value, 42);
524 ASSERT_EQ(count, 1);
525}
526
527} // 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_PRED(_exec_, _label_)
#define MATCHER_FOR_DEV_ID(_type_, _exec_)
#define MATCHER_FOR_BEGIN_SINGLE(_exec_, _label_)
#define MATCHER_FOR_WAIT_EVENT(_record_event_variant_)
#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 >, BeginDeepCopyEvent, BeginFenceEvent, BeginParallelForEvent, BeginParallelReduceEvent, 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
constexpr parallel_reduce_t parallel_reduce
auto ABeginParallelForEvent(Matchers &&... matchers)
::testing::Types< typename ParallelReduceTest::view_s_t, typename ParallelReduceTest::view_um_s_t > pred_managed_and_unmanaged_types_t
Kokkos::Execution::ExecutionSpaceImpl::ParallelReduceSender< Kokkos::Execution::parallel_reduce_t, Sndr, Exec, Kokkos::Execution::Impl::ParallelReduceData< Functor, Policy, Reducer, Kokkos::Execution::Impl::Label< Label > > > pred_impl_t
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
Kokkos::Execution::Impl::ParallelReduceSender< Sndr, Kokkos::Execution::Impl::ParallelReduceData< Functor, Policy, Reducer, Kokkos::Execution::Impl::Label< Label > > > pred_generic_t
consteval bool test_sndr_is_transformable()
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)
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_reduce construct.
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:33
Load the value at data and check it is equal to prev. Then, add value to it.
Can be used in a parallel_reduce to find the greater of the values in data alongside its index.
Definition maxloc.hpp:15
Similar to ReduceIndices, with a team policy.
A minimal tracking allocator.