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
57TEST(RecordEvent, description) {
58 const Kokkos::Execution::Impl::RecordEvent event{.dev_id = 42, .event_id = 1337};
59
60 std::ostringstream oss;
61 oss << event;
62
63 ASSERT_EQ(oss.str(), "RecordEvent: {dev_id = 42, event_id = 1337}");
64}
65
67TEST(WaitEvent, description) {
68 const Kokkos::Execution::Impl::WaitEvent event{.event_id = 1337};
69
70 std::ostringstream oss;
71 oss << event;
72
73 ASSERT_EQ(oss.str(), "WaitEvent: {dev_id = 0, event_id = 1337}");
74}
75
77TEST_F(EventTest, record_and_wait) {
78 const auto recorded_events = recorder_listener_t::record([this]() {
82 });
83
84 ASSERT_THAT(recorded_events, testing::SizeIs(2));
85 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
86 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
87}
88
90TEST_F(EventTest, record_but_dont_wait) {
91 const auto recorded_events = recorder_listener_t::record([this]() {
93 Kokkos::parallel_for(Kokkos::RangePolicy(exec, 0, 1), Tests::Utils::Functors::NoOp{});
95
96 exec.fence("some-label");
97 });
98
99 ASSERT_THAT(recorded_events, testing::SizeIs(2));
100 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
101 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_BEGIN_FENCE(exec, "some-label"));
102}
103
105TEST_F(EventTest, record_and_wait_many_times) {
106 const auto recorded_events = recorder_listener_t::record([this]() {
114 });
115
116 ASSERT_THAT(recorded_events, ::testing::SizeIs(6));
117 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
118 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
119 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
120 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
121 ASSERT_THAT(recorded_events.at(4), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
122 ASSERT_THAT(recorded_events.at(5), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
123}
124
126TEST_F(EventTest, record_and_wait_and_record_and_wait) {
127 const auto recorded_events = recorder_listener_t::record([this]() {
133 });
134
135 ASSERT_THAT(recorded_events, ::testing::SizeIs(4));
136
137 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
138 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
139
140 ASSERT_THAT(recorded_events.at(2), MATCHER_FOR_RECORD_EVENT(exec));
141 ASSERT_THAT(recorded_events.at(3), MATCHER_FOR_WAIT_EVENT(recorded_events.at(2)));
142}
143
145TEST_F(EventTest, uniqueness) {
146 const auto recorded_events = recorder_listener_t::record([this]() {
149 Kokkos::parallel_for(Kokkos::RangePolicy(exec, 0, 1), Tests::Utils::Functors::NoOp{});
151
152 exec.fence("some-label");
153 });
154
155 ASSERT_THAT(recorded_events, ::testing::SizeIs(3));
156
157 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec));
158
159 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_RECORD_EVENT(exec));
160
161 ASSERT_NE(
162 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(0)).event_id,
163 std::get<Kokkos::Execution::Impl::RecordEvent>(recorded_events.at(1)).event_id);
164
165 ASSERT_THAT(recorded_events.back(), MATCHER_FOR_BEGIN_FENCE(exec, "some-label"));
166}
167
169TEST_F(EventTest, default_instance) {
170 const TEST_EXECUTION_SPACE default_exec{};
171
172 const auto recorded_events = recorder_listener_t::record([&default_exec]() {
173 Kokkos::parallel_for(Kokkos::RangePolicy(default_exec, 0, 1), Tests::Utils::Functors::NoOp{});
175 Kokkos::Execution::Impl::record(event, default_exec);
177 });
178
179 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
180 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(default_exec));
181 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EVENT(recorded_events.at(0)));
182}
183
185TEST_F(EventTest, wait_exec_event_for_same_type) {
186 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
187
188 const auto recorded_events = recorder_listener_t::record([&exec_A, &exec_B]() {
190 Kokkos::Execution::Impl::record(event_A, exec_A);
191 Kokkos::Execution::Impl::wait(exec_B, event_A);
192 });
193
194 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
195 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(exec_A));
196 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_B, recorded_events.at(0)));
197}
198
200TEST_F(EventTest, wait_exec_event_different_type) {
201 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::DefaultHostExecutionSpace>) {
202 GTEST_SKIP() << "The default host execution space is the same type as the test execution space.";
203 }
204
205 const Kokkos::DefaultHostExecutionSpace exec_h;
206
207 const auto recorded_events = recorder_listener_t::record([this, &exec_h]() {
210 Kokkos::Execution::Impl::wait(exec_h, event_d);
211 });
212
213 ASSERT_THAT(recorded_events, ::testing::SizeIs(2));
214 ASSERT_THAT(recorded_events.at(0), MATCHER_FOR_RECORD_EVENT(this->exec));
215 ASSERT_THAT(recorded_events.at(1), MATCHER_FOR_WAIT_EXEC_EVENT(exec_h, recorded_events.at(0)));
216}
217
224TEST_F(EventTest, works_intended_usage_pattern) {
225 constexpr size_t size = 128;
226
227 const auto [exec_A, exec_B, exec_C] = Kokkos::Experimental::partition_space(exec, 1, 1, 1);
228
229 const Kokkos::View<int, TEST_EXECUTION_SPACE> data(Kokkos::view_alloc(exec_A, "data"));
230 const auto data_h = Kokkos::create_mirror_view(Kokkos::WithoutInitializing, data);
231
232 std::optional<Kokkos::Execution::Impl::Event<TEST_EXECUTION_SPACE>> event_A;
233
234 stdexec::run_loop loop;
235 std::thread consumer([&] { loop.run(); });
236
237 Kokkos::parallel_for(
238 Kokkos::RangePolicy(exec_A, 0, size), KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
239 event_A.emplace();
240 Kokkos::Execution::Impl::record(*event_A, exec_A);
241
242 bool upon_error = false;
243
244 auto opstate = stdexec::connect(
245 stdexec::schedule(loop.get_scheduler()) | stdexec::then([&] {
246 event_A->wait();
247 event_A.reset();
248 Kokkos::parallel_for(
249 Kokkos::RangePolicy(exec_B, 0, size),
250 KOKKOS_LAMBDA(const auto idx) { Kokkos::atomic_add(&data(), idx); });
252 Kokkos::Execution::Impl::record(event_B, exec_B);
253 Kokkos::Execution::Impl::wait(exec_C, event_B);
254 Kokkos::parallel_for(
255 Kokkos::RangePolicy(exec_C, 0, 1),
256 KOKKOS_LAMBDA(const auto) { Kokkos::atomic_compare_exchange(&data(), size * (size - 1), 1); });
257 Kokkos::deep_copy(exec_C, data_h, data);
258 exec_C.fence("wait for the deep copy to complete");
259 loop.finish();
260 }) | stdexec::upon_error([&](const auto& eptr) {
261 upon_error = true;
262 try {
263 std::rethrow_exception(eptr);
264 } catch (const std::exception& exc) {
265 Kokkos::printf("%s\n", exc.what()); // NOLINT(modernize-use-std-print)
266 }
267 loop.finish();
268 }),
269 Tests::Utils::SinkReceiver{});
270
271 opstate.start();
272
273 consumer.join();
274
275 ASSERT_FALSE(event_A.has_value());
276
278#if defined(KOKKOS_ENABLE_THREADS)
279 if constexpr (std::same_as<TEST_EXECUTION_SPACE, Kokkos::Threads>) {
280 ASSERT_EQ(data_h(), size / 2 * (size - 1));
281 ASSERT_TRUE(upon_error);
282 } else
283#endif
284 {
285 ASSERT_EQ(data_h(), 1);
286 ASSERT_FALSE(upon_error);
287 }
288}
289
290} // 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:24
void record(Event< Exec > &event, const Exec &exec)
Record event on exec.
Definition event.hpp:138
void wait(const Event< Exec > &event)
Wait for event to complete.
Definition event.hpp:145
consteval bool test_models_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:108
Event to be sent to Kokkos::utils::callbacks::dispatch when calling record.
Definition event.hpp:52
Event to be sent to Kokkos::utils::callbacks::dispatch when calling wait.
Definition event.hpp:73