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}
55
57template <Kokkos::ExecutionSpace Exec>
58consteval bool test_has_exec_wait_event() {
59#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
60 if constexpr (std::same_as<Exec, Kokkos::DefaultExecutionSpace>) {
62 return true;
63 } else
64#endif
65#if defined(KOKKOS_ENABLE_HPX)
66 if constexpr (std::same_as<Exec, Kokkos::Experimental::HPX>) {
68 return true;
69 } else
70#endif
71 {
73 return true;
74 }
75}
77
79TEST(RecordEvent, description) {
80 const Kokkos::Execution::Impl::RecordEvent event{.dev_id = 42, .event_id = 1337};
81
82 std::ostringstream oss;
83 oss << event;
84
85 ASSERT_EQ(oss.str(), "RecordEvent: {dev_id = 42, event_id = 1337}");
86}
87
89TEST(WaitEvent, description) {
90 const Kokkos::Execution::Impl::WaitEvent event{.event_id = 1337};
91
92 std::ostringstream oss;
93 oss << event;
94
95 ASSERT_EQ(oss.str(), "WaitEvent: {dev_id = 0, event_id = 1337}");
96}
97
99TEST_F(EventTest, record_and_wait) {
100 const auto recorded_events = recorder_listener_t::record([this]() {
104 });
105
106 ASSERT_THAT(recorded_events, testing::SizeIs(2));
107 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
108 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
109}
110
112TEST_F(EventTest, record_but_dont_wait) {
113 const auto recorded_events = recorder_listener_t::record([this]() {
115 Kokkos::parallel_for(Kokkos::RangePolicy(exec, 0, 1), Tests::Utils::Functors::NoOp{});
117
118 exec.fence("some-label");
119 });
120
121 ASSERT_THAT(recorded_events, testing::SizeIs(2));
122 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
123 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_BEGIN_FENCE(exec, "some-label"));
124}
125
127TEST_F(EventTest, record_and_wait_and_record_and_wait) {
128 const auto recorded_events = recorder_listener_t::record([this]() {
134 });
135
136 ASSERT_THAT(recorded_events, ::testing::SizeIs(4));
137
138 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
139 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
140
141 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_RECORD_EVENT(exec));
142 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)));
143}
144
146TEST_F(EventTest, uniqueness) {
147 const auto recorded_events = recorder_listener_t::record([this]() {
150 Kokkos::parallel_for(Kokkos::RangePolicy(exec, 0, 1), Tests::Utils::Functors::NoOp{});
152
153 exec.fence("some-label");
154 });
155
156 ASSERT_THAT(recorded_events, ::testing::SizeIs(3));
157
158 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
159
160 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_RECORD_EVENT(exec));
161
162 ASSERT_NE(
163 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(0)).event_id,
164 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(1)).event_id);
165
166 ASSERT_THAT(recorded_events.back(), MATCHER_FOR_BEGIN_FENCE(exec, "some-label"));
167}
168
170TEST_F(EventTest, default_instance) {
171 const TEST_EXECUTION_SPACE default_exec{};
172
173 const auto recorded_events = recorder_listener_t::record([&default_exec]() {
174 Kokkos::parallel_for(Kokkos::RangePolicy(default_exec, 0, 1), Tests::Utils::Functors::NoOp{});
176 Kokkos::Execution::Impl::record(event, default_exec);
178 });
179
180 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
181 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(default_exec));
182 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
183}
184
186TEST_F(EventTest, wait_exec_event_for_same_type) {
187 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
188
189 const auto recorded_events = recorder_listener_t::record([&exec_A, &exec_B]() {
191 Kokkos::Execution::Impl::record(event_A, exec_A);
192 Kokkos::Execution::Impl::wait(exec_B, event_A);
193
195 exec_B.fence();
196 });
197
198 ASSERT_THAT(recorded_events, ::testing::SizeIs(3));
199 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec_A));
200 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(0)));
201}
202
204TEST_F(EventTest, wait_exec_events_for_same_type) {
205 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
206
207 const auto recorded_events = recorder_listener_t::record([&exec_A, &exec_B]() {
208 Kokkos::Execution::Impl::Event<TEST_EXECUTION_SPACE> event_A_0, event_A_1, event_A_2;
209 Kokkos::Execution::Impl::record(event_A_0, exec_A);
210 Kokkos::Execution::Impl::record(event_A_1, exec_A);
211 Kokkos::Execution::Impl::record(event_A_2, exec_A);
212 Kokkos::Execution::Impl::wait(exec_B, event_A_0, event_A_1, event_A_2);
213 });
214
215 ASSERT_THAT(recorded_events, ::testing::SizeIs(6));
216 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec_A));
217 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_RECORD_EVENT(exec_A));
218 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_RECORD_EVENT(exec_A));
219 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(0)));
220 ASSERT_THAT(recorded_events.at(4), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(1)));
221 ASSERT_THAT(recorded_events.at(5), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(2)));
222}
223
225TEST_F(EventTest, wait_exec_event_different_type) {
226 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::DefaultHostExecutionSpace>) {
227 GTEST_SKIP() << "The default host execution space is the same type as the test execution space.";
228 }
229
230 const Kokkos::DefaultHostExecutionSpace exec_h;
231
232 const auto recorded_events = recorder_listener_t::record([this, &exec_h]() {
235 Kokkos::Execution::Impl::wait(exec_h, event_d);
236 });
237
238 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
239 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(this->exec));
240 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(0)));
241}
242
249TEST_F(EventTest, works_intended_usage_pattern) {
250 constexpr size_t size = 128;
251
252 const auto [exec_A, exec_B, exec_C] = Kokkos::Experimental::partition_space(exec, 1, 1, 1);
253
254 const Kokkos::View<int, TEST_EXECUTION_SPACE> data(Kokkos::view_alloc(exec_A, "data"));
255 const auto data_h = Kokkos::create_mirror_view(Kokkos::WithoutInitializing, data);
256
257 std::optional<Kokkos::Execution::Impl::Event<TEST_EXECUTION_SPACE>> event_A;
258
259 stdexec::run_loop loop;
260 std::thread consumer([&] { loop.run(); });
261
262 Kokkos::parallel_for(
263 Kokkos::RangePolicy(exec_A, 0, size), KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
264 event_A.emplace();
265 Kokkos::Execution::Impl::record(*event_A, exec_A);
266
267 bool upon_error = false;
268
269 auto opstate = stdexec::connect(
270 stdexec::schedule(loop.get_scheduler()) | stdexec::then([&] {
271 event_A->wait();
272 event_A.reset();
273 Kokkos::parallel_for(
274 Kokkos::RangePolicy(exec_B, 0, size),
275 KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
277 Kokkos::Execution::Impl::record(event_B, exec_B);
278 Kokkos::Execution::Impl::wait(exec_C, event_B);
279 Kokkos::parallel_for(
280 Kokkos::RangePolicy(exec_C, 0, 1),
281 KOKKOS_LAMBDA(const auto) { Kokkos::atomic_compare_exchange(&data(), size * (size - 1), 1); });
282 Kokkos::deep_copy(exec_C, data_h, data);
283 exec_C.fence("wait for the deep copy to complete");
284 loop.finish();
285 }) | stdexec::upon_error([&](const auto& eptr) {
286 upon_error = true;
287 try {
288 std::rethrow_exception(eptr);
289 } catch (const std::exception& exc) {
290 Kokkos::printf("%s\n", exc.what()); // NOLINT(modernize-use-std-print)
291 }
292 loop.finish();
293 }),
294 Tests::Utils::SinkReceiver{});
295
296 opstate.start();
297
298 consumer.join();
299
300 ASSERT_FALSE(event_A.has_value());
301
303#if defined(KOKKOS_ENABLE_THREADS)
304 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Threads>) {
305 ASSERT_EQ(data_h(), size / 2 * (size - 1));
306 ASSERT_TRUE(upon_error);
307 } else
308#endif
309 {
310 ASSERT_EQ(data_h(), 1);
311 ASSERT_FALSE(upon_error);
312 }
313}
314
315} // 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:26
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:140
void wait(const Event< Exec > &... events)
Wait for events to complete.
Definition event.hpp:152
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:110
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