kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_event.cpp
Go to the documentation of this file.
1#include "gtest/gtest.h"
2
5
7
9
14
25
26#if !defined(KOKKOS_EXECUTION_ENABLE_EVENT_DISPATCH)
27# error "This is not supported."
28#endif
29
30namespace Tests::Impl {
31
32using namespace Kokkos::utils::callbacks;
33
46
48template <Kokkos::ExecutionSpace Exec>
49consteval bool test_models_event() {
51
52 return true;
53}
54
56
58template <Kokkos::ExecutionSpace Exec>
59consteval bool test_has_exec_wait_event() {
60#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
61 if constexpr (std::same_as<Exec, Kokkos::DefaultExecutionSpace>) {
63 return true;
64 } else
65#endif
66#if defined(KOKKOS_ENABLE_HPX)
67 if constexpr (std::same_as<Exec, Kokkos::Experimental::HPX>) {
69 return true;
70 } else
71#endif
72 {
74 return true;
75 }
76}
77
79
81TEST(RecordEvent, description) {
82 const Kokkos::Execution::Impl::RecordEvent event{.dev_id = 42, .event_id = 1337};
83
84 std::ostringstream oss;
85 oss << event;
86
87 ASSERT_EQ(oss.str(), "RecordEvent: {dev_id = 42, event_id = 1337}");
88}
89
91TEST(WaitEvent, description) {
92 const Kokkos::Execution::Impl::WaitEvent event{.event_id = 1337};
93
94 std::ostringstream oss;
95 oss << event;
96
97 ASSERT_EQ(oss.str(), "WaitEvent: {dev_id = 0, event_id = 1337}");
98}
99
101TEST_F(EventTest, record_and_wait) {
102 const auto recorded_events = recorder_listener_t::record([this]() {
106 });
107
108 ASSERT_THAT(recorded_events, testing::SizeIs(2));
109 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
110 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
111}
112
114TEST_F(EventTest, record_and_wait_and_record_and_wait) {
115 const auto recorded_events = recorder_listener_t::record([this]() {
121 });
122
123 ASSERT_THAT(recorded_events, ::testing::SizeIs(4));
124
125 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
126 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
127
128 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_RECORD_EVENT(exec));
129 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)));
130}
131
133TEST_F(EventTest, uniqueness) {
134 const auto recorded_events = recorder_listener_t::record([this]() {
137 Kokkos::parallel_for(Kokkos::RangePolicy(exec, 0, 1), Tests::Utils::Functors::NoOp{});
139
140 Kokkos::Execution::Impl::wait(event_before);
142 });
143
144 ASSERT_THAT(recorded_events, ::testing::SizeIs(4));
145
146 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
147
148 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_RECORD_EVENT(exec));
149
150 ASSERT_NE(
151 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(0)).event_id,
152 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(1)).event_id);
153
154 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
155 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EVENT(recorded_events.at(1)));
156}
157
159TEST_F(EventTest, default_instance) {
160 const TEST_EXECUTION_SPACE default_exec{};
161
162 const auto recorded_events = recorder_listener_t::record([&default_exec]() {
163 Kokkos::parallel_for(Kokkos::RangePolicy(default_exec, 0, 1), Tests::Utils::Functors::NoOp{});
165 Kokkos::Execution::Impl::record(event, default_exec);
167 });
168
169 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
170 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(default_exec));
171 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
172}
173
175TEST_F(EventTest, wait_exec_event_for_same_type) {
176 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
177
178 const auto recorded_events = recorder_listener_t::record([&exec_A, &exec_B]() {
180 Kokkos::Execution::Impl::record(event_A, exec_A);
181 Kokkos::Execution::Impl::wait(exec_B, event_A);
182
184 exec_B.fence("some-label");
185 });
186
187 ASSERT_THAT(recorded_events, ::testing::SizeIs(3));
188 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec_A));
189 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(0)));
190 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_BEGIN_FENCE(exec_B, "some-label"));
191}
192
194TEST_F(EventTest, wait_exec_events_for_same_type) {
195 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
196
197 const auto recorded_events = recorder_listener_t::record([&exec_A, &exec_B]() {
198 Kokkos::Execution::Impl::Event<TEST_EXECUTION_SPACE> event_A_0, event_A_1, event_A_2;
199 Kokkos::Execution::Impl::record(event_A_0, exec_A);
200 Kokkos::Execution::Impl::record(event_A_1, exec_A);
201 Kokkos::Execution::Impl::record(event_A_2, exec_A);
202 Kokkos::Execution::Impl::wait(exec_B, event_A_0, event_A_1, event_A_2);
203
205 exec_B.fence("some-label");
206 });
207
208 ASSERT_THAT(recorded_events, ::testing::SizeIs(7));
209 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec_A));
210 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_RECORD_EVENT(exec_A));
211 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_RECORD_EVENT(exec_A));
212 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(0)));
213 ASSERT_THAT(recorded_events.at(4), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(1)));
214 ASSERT_THAT(recorded_events.at(5), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(2)));
215 ASSERT_THAT(recorded_events.at(6), MATCHER_FOR_BEGIN_FENCE(exec_B, "some-label"));
216}
217
219TEST_F(EventTest, wait_exec_event_different_type) {
220 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::DefaultHostExecutionSpace>) {
221 GTEST_SKIP() << "The default host execution space is the same type as the test execution space.";
222 }
223
224 const Kokkos::DefaultHostExecutionSpace exec_h;
225
226 const auto recorded_events = recorder_listener_t::record([this, &exec_h]() {
229 Kokkos::Execution::Impl::wait(exec_h, event_d);
230
232 exec_h.fence("some-label");
233 });
234
235 ASSERT_THAT(recorded_events, ::testing::SizeIs(3));
236 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(this->exec));
237 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(0)));
238 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_BEGIN_FENCE(exec_h, "some-label"));
239}
240
247TEST_F(EventTest, works_intended_usage_pattern) {
248 constexpr size_t size = 128;
249
250 const auto [exec_A, exec_B, exec_C] = Kokkos::Experimental::partition_space(exec, 1, 1, 1);
251
252 const Kokkos::View<int, TEST_EXECUTION_SPACE> data(Kokkos::view_alloc(exec_A, "data"));
253 const auto data_h = Kokkos::create_mirror_view(Kokkos::WithoutInitializing, data);
254
255 std::optional<Kokkos::Execution::Impl::Event<TEST_EXECUTION_SPACE>> event_A;
256
257 stdexec::run_loop loop;
258 std::thread consumer([&] { loop.run(); });
259
260 Kokkos::parallel_for(
261 Kokkos::RangePolicy(exec_A, 0, size), KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
262 event_A.emplace();
263 Kokkos::Execution::Impl::record(*event_A, exec_A);
264
265 bool upon_error = false;
266
267 auto op_state = stdexec::connect(
268 stdexec::schedule(loop.get_scheduler()) | stdexec::then([&] {
269 event_A->wait();
270 event_A.reset();
271 Kokkos::parallel_for(
272 Kokkos::RangePolicy(exec_B, 0, size),
273 KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
275 Kokkos::Execution::Impl::record(event_B, exec_B);
276 Kokkos::Execution::Impl::wait(exec_C, event_B);
277 Kokkos::parallel_for(
278 Kokkos::RangePolicy(exec_C, 0, 1),
279 KOKKOS_LAMBDA(const auto) { Kokkos::atomic_compare_exchange(&data(), size * (size - 1), 1); });
280 Kokkos::deep_copy(exec_C, data_h, data);
281 exec_C.fence("wait for the deep copy to complete");
282 loop.finish();
283 }) | stdexec::upon_error([&](const auto& eptr) {
284 upon_error = true;
285 try {
286 std::rethrow_exception(eptr);
287 } catch (const std::exception& exc) {
288 Kokkos::printf("%s\n", exc.what()); // NOLINT(modernize-use-std-print)
289 }
290 loop.finish();
291 }),
292 Tests::Utils::SinkReceiver{});
293
294 op_state.start();
295
296 consumer.join();
297
298 ASSERT_FALSE(event_A.has_value());
299
301#if defined(KOKKOS_ENABLE_THREADS)
302 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Threads>) {
303 ASSERT_EQ(data_h(), size / 2 * (size - 1));
304 ASSERT_TRUE(upon_error);
305 } else
306#endif
307 {
308 ASSERT_EQ(data_h(), 1);
309 ASSERT_FALSE(upon_error);
310 }
311}
312
313} // namespace Tests::Impl
#define MATCHER_FOR_WAIT_EVENT(_record_event_variant_)
#define MATCHER_FOR_WAIT_EXEC_EVENT(_exec_, _record_event_variant_)
#define MATCHER_FOR_RECORD_EVENT(_exec_)
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
Fixture that enables callbacks with Kokkos::utils::tests::scoped::callbacks::Manager.
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, Kokkos::Execution::Impl::RecordEvent, Kokkos::Execution::Impl::WaitEvent > recorder_listener_t
Constrain an EventType type to be a valid event type for Exec execution space type.
Definition event.hpp:31
Determine if the Kokkos backend can enqueue a wait for an event into an execution space instance.
void record(Event< Exec > &event, const Exec &exec)
Record event on exec.
Definition event.hpp:145
void wait(const Event< Exec > &... events)
Wait for events to complete.
Definition event.hpp:157
consteval bool test_models_event()
consteval bool test_has_exec_wait_event()
Matcher to filter out events that are just noise for tests.
An event that can be recorded on an execution space instance.
Definition event.hpp:115
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