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
18
29
31
32using namespace Kokkos::utils::callbacks;
33
47
52template <template <typename...> class SndrAdptr, bool IsDispatchingSender, typename... Args>
53consteval bool test_sndr_traits() {
55 using schd_sndr_t = typename ParallelForTest::schedule_sender_t;
56
58 using label_t = std::string;
60 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
61 using pfor_sndr_t = SndrAdptr<Args..., schd_sndr_t, label_t, functor_t, policy_t>;
62
65 static_assert(std::same_as<Kokkos::Execution::Impl::exec_of_t<pfor_sndr_t>, TEST_EXECUTION_SPACE>);
66
68 static_assert(Kokkos::Execution::Impl::dispatching_sender<pfor_sndr_t> == IsDispatchingSender);
69
71 static_assert(!stdexec::dependent_sender<pfor_sndr_t>);
72 static_assert(
73 stdexec::get_completion_signatures<pfor_sndr_t>()
74 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
75
77 static_assert(std::same_as<
78 stdexec::__completion_domain_of_t<stdexec::set_value_t, pfor_sndr_t, stdexec::env<>>,
80 >);
81
83 static_assert(std::same_as<
86 >);
87
89 static_assert(stdexec::sender_to<pfor_sndr_t, Tests::Utils::SinkReceiver>);
90
91 static_assert(std::same_as<
92 stdexec::transform_sender_result_t<pfor_sndr_t, stdexec::env_of_t<Tests::Utils::SinkReceiver>>,
95 schd_sndr_t,
96 label_t,
97 functor_t,
98 policy_t
99 >
100 >);
101
103 static_assert(stdexec::__nothrow_connectable<pfor_sndr_t, Tests::Utils::SinkReceiver>);
104
105 return true;
106}
108static_assert(test_sndr_traits<
110 false,
112>());
113
115consteval bool test_sndr_decomposition() {
117 using schd_sndr_t = typename ParallelForTest::schedule_sender_t;
118
120 using label_t = std::string;
122 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
124
126 static_assert(stdexec::__sender_for<pfor_sndr_t, Kokkos::Execution::parallel_for_t>);
127
128 static_assert(std::same_as<
129 stdexec::__data_of<pfor_sndr_t>,
131 >);
132
133 static_assert(stdexec::__nbr_children_of<pfor_sndr_t> == 1);
134 static_assert(std::same_as<stdexec::__child_of<pfor_sndr_t>, schd_sndr_t>);
135
137 static_assert(stdexec::__applicable<
139 pfor_sndr_t,
140 const stdexec::env<>&
141 >);
142
143 return true;
144}
145static_assert(test_sndr_decomposition());
146
148template <typename ViewType>
149consteval bool test_closure_traits() {
151 using policy_t = Kokkos::RangePolicy<TEST_EXECUTION_SPACE>;
153
156
157 static_assert(std::is_nothrow_move_constructible_v<closure_t>);
158
159 return true;
160}
162static_assert(test_closure_traits<std::span<int>>());
163
168 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>,
170>());
171
173TEST_F(ParallelForTest, team_policy) {
174 constexpr int size = 32;
175
176 const auto [num_teams, team_size] = [&]() {
178#if defined(KOKKOS_ENABLE_HPX)
179 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Experimental::HPX>) {
180 return std::make_tuple(size, 1);
181 }
182#endif
183 const int team_size_ = std::bit_floor(static_cast<unsigned short>(std::min(exec.concurrency(), size / 2)));
184 return std::make_tuple(size / team_size_, team_size_);
185 }();
186
187 ASSERT_EQ(team_size * num_teams, size);
188
189 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
190
191 const context_t esc{exec};
192
193 auto chain = stdexec::schedule(esc.get_scheduler())
195 "hello from pfor",
196 Kokkos::TeamPolicy<TEST_EXECUTION_SPACE>(num_teams, team_size),
197 Tests::Utils::Functors::SumIndices{.data = witness});
198
199 stdexec::sync_wait(std::move(chain));
200
201 ASSERT_EQ(witness(), size / 2 * (size - 1));
202}
203
204template <typename ViewType, Kokkos::ExecutionSpace Exec>
206 const size_t size,
207 const ViewType& witness,
208 const Kokkos::Execution::ExecutionSpaceContext<Exec>& esc) -> stdexec::sender auto {
209 auto chain = stdexec::schedule(esc.get_scheduler())
211 "passing label, execution policy and functor",
212 Kokkos::RangePolicy<Exec>(0, size),
213 Tests::Utils::Functors::SumIndices{.data = witness})
215 Kokkos::RangePolicy<Exec>(0, size), Tests::Utils::Functors::SumIndices{.data = witness});
216
217 if constexpr (std::same_as<Exec, Kokkos::DefaultExecutionSpace>) {
218 return std::move(chain)
220 "passing label, work count and functor", size, Tests::Utils::Functors::SumIndices{.data = witness})
222 } else {
223 return chain;
224 }
225}
226
229 constexpr size_t size = 10;
230
231 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
232
233 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(
235
236 unsigned short int ievent = 0;
237
238 ASSERT_GE(recorded_events.size(), 3);
239
241
242 ASSERT_THAT(
243 recorded_events,
244 ElementAt<variant_t>(ievent++, MATCHER_FOR_BEGIN_PFOR(exec, "passing label, execution policy and functor")));
245 ASSERT_THAT(
246 recorded_events,
247 ElementAt<variant_t>(ievent++, MATCHER_FOR_BEGIN_PFOR(exec, Kokkos::Impl::TypeInfo<functor_t>::name())));
248
249 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::DefaultExecutionSpace>) {
250 ASSERT_THAT(
251 recorded_events.at(ievent++), MATCHER_FOR_BEGIN_PFOR(exec, "passing label, work count and functor"));
252 ASSERT_THAT(
253 recorded_events.at(ievent++), MATCHER_FOR_BEGIN_PFOR(exec, Kokkos::Impl::TypeInfo<functor_t>::name()));
254 }
255
256 ASSERT_THAT(recorded_events.at(ievent), MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "sync_wait")));
257
258 ASSERT_EQ(witness(), ievent * size / 2 * (size - 1));
259}
260
262TEST_F(ParallelForTest, two_parallel_regions) {
263 constexpr size_t size = 10;
264
265 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
266
267 const context_t esc{exec};
268
269 auto chain = stdexec::schedule(esc.get_scheduler())
271 std::format("{}: hello from pfor", Kokkos::Impl::TypeInfo<TEST_EXECUTION_SPACE>::name()),
272 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
273 Tests::Utils::Functors::SumIndices{.data = witness})
274 | stdexec::then(
276 .prev = size / 2 * (size - 1), .value = 4, .data = witness.data()})
278 std::format("{}: hello again from pfor", Kokkos::Impl::TypeInfo<TEST_EXECUTION_SPACE>::name()),
279 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, 2 * size),
280 Tests::Utils::Functors::SumIndices{.data = witness});
281
282 ASSERT_THAT(
284 testing::ElementsAre(
285 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
287 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello again from pfor")),
289
290 ASSERT_EQ(witness(), size / 2 * (size - 1) + 4 + 2 * size * (2 * size - 1) / 2);
291}
292
294TEST_F(ParallelForTest, starts_on_parallel_region) {
295 constexpr size_t size = 10;
296
297 const view_s_t witness(Kokkos::view_alloc(exec, "data - shared space"));
298
299 auto chain = stdexec::just()
301 std::format("{}: hello from pfor", Kokkos::Impl::TypeInfo<TEST_EXECUTION_SPACE>::name()),
302 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
303 Tests::Utils::Functors::SumIndices{.data = witness});
304
305 using chain_t = decltype(chain);
306
307 static_assert(
308 stdexec::get_completion_signatures<chain_t>()
309 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
311 static_assert(
312 std::same_as<stdexec::__completion_domain_of_t<stdexec::set_value_t, chain_t>, stdexec::indeterminate_domain<>>);
313
314 const context_t esc{exec};
315 auto starts_on = stdexec::starts_on(esc.get_scheduler(), std::move(chain));
316
317 using starts_on_t = decltype(starts_on);
318
319 static_assert(stdexec::__has_eptr_completion<chain_t>);
320 static_assert(!stdexec::dependent_sender<starts_on_t>);
321 static_assert(
322 stdexec::get_completion_signatures<starts_on_t>()
323 == stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{});
324
331 static_assert(stdexec::__is_instance_of<
332 stdexec::transform_sender_result_t<
333 decltype(starts_on),
334 stdexec::env_of_t<Kokkos::Execution::Impl::SyncWait::Receiver<TEST_EXECUTION_SPACE>>
335 >,
336 stdexec::__seq::__sndr
337 >);
338
339 const auto recorded_events = Tests::Utils::record_sync_wait<recorder_listener_t>(std::move(starts_on));
340
341 ASSERT_THAT(recorded_events, [&]() {
343 return testing::ElementsAre(
344 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
346 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
347 } else {
348 return testing::ElementsAre(
349 MATCHER_FOR_BEGIN_PFOR(exec, dispatch_label(exec, "hello from pfor")),
350 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
351 }
352 }());
353
354 ASSERT_EQ(witness(), size / 2 * (size - 1));
355}
356
358TEST_F(ParallelForTest, forwarding_env) {
359 constexpr size_t size = 10;
360
361 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
362
363 std::atomic<size_t> count = 0;
364
365 int value;
366
367 const context_t esc{exec};
368
369 stdexec::sender auto sndr =
370 stdexec::read_env(stdexec::get_allocator)
371 | stdexec::then([&value](auto allocator) { value = Tests::Utils::round_trip_allocate(allocator, 42); })
372 | stdexec::continues_on(esc.get_scheduler())
375 "my pfor",
376 Kokkos::RangePolicy<TEST_EXECUTION_SPACE>(0, size),
378 | stdexec::write_env(stdexec::prop{stdexec::get_allocator, Tests::Utils::TrackingAllocator<int>{&count}});
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(sndr));
383
384 ASSERT_THAT(recorded_events, [&]() {
386 return testing::ElementsAre(
387 MATCHER_FOR_BEGIN_PFOR(exec, "my pfor"),
389 MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
390 } else {
391 return testing::ElementsAre(
392 MATCHER_FOR_BEGIN_PFOR(exec, "my pfor"),
393 MATCHER_FOR_BEGIN_FENCE(exec, dispatch_label(exec, "after dispatch")));
394 }
395 }());
396
397 ASSERT_EQ(data(), size / 2 * (size - 1));
398
399 ASSERT_EQ(value, 42);
400 ASSERT_EQ(count, 1);
401}
402
403} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and 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_)
typename recorder_listener_t::event_variant_t variant_t
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, BeginParallelForEvent, 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 parallel_for_t parallel_for
auto ElementAt(const size_t index, ElementMatcher &&matcher)
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_decomposition()
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< Exec >
Scheduler for a Kokkos execution space.
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:54
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:75
Custom algorithm for the Kokkos::parallel_for construct.
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
Load the value at data and check it is equal to prev. Then, add value to it.
A minimal tracking allocator.