DPC++ Runtime
Runtime libraries for oneAPI DPC++
event_impl.cpp
Go to the documentation of this file.
1 //==---------------- event_impl.cpp - SYCL event ---------------------------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <detail/event_impl.hpp>
10 #include <detail/event_info.hpp>
11 #include <detail/plugin.hpp>
12 #include <detail/queue_impl.hpp>
14 #include <sycl/context.hpp>
15 #include <sycl/device_selector.hpp>
16 
17 #include "detail/config.hpp"
18 
19 #include <chrono>
20 
21 #ifdef XPTI_ENABLE_INSTRUMENTATION
22 #include "xpti/xpti_trace_framework.hpp"
23 #include <atomic>
24 #include <detail/xpti_registry.hpp>
25 #include <sstream>
26 #endif
27 
28 namespace sycl {
30 namespace detail {
31 #ifdef XPTI_ENABLE_INSTRUMENTATION
32 extern xpti::trace_event_data_t *GSYCLGraphEvent;
33 #endif
34 
35 // If we do not yet have a context, use the default one.
36 void event_impl::ensureContextInitialized() {
37  if (MIsContextInitialized)
38  return;
39 
40  if (MHostEvent) {
41  QueueImplPtr HostQueue = Scheduler::getInstance().getDefaultHostQueue();
42  this->setContextImpl(detail::getSyclObjImpl(HostQueue->get_context()));
43  } else {
44  const device &SyclDevice = default_selector().select_device();
45  this->setContextImpl(detail::queue_impl::getDefaultOrNew(
46  detail::getSyclObjImpl(SyclDevice)));
47  }
48 }
49 
50 bool event_impl::is_host() {
51  // Treat all devices that don't support interoperability as host devices to
52  // avoid attempts to call method get on such events.
53  return MHostEvent;
54 }
55 
56 event_impl::~event_impl() {
57  if (MEvent)
59 }
60 
61 void event_impl::waitInternal() {
62  if (!MHostEvent && MEvent) {
63  // Wait for the native event
64  getPlugin().call<PiApiKind::piEventsWait>(1, &MEvent);
65  } else if (MState == HES_Discarded) {
66  // Waiting for the discarded event is invalid
67  throw sycl::exception(
68  make_error_code(errc::invalid),
69  "waitInternal method cannot be used for a discarded event.");
70  } else if (MState != HES_Complete) {
71  // Wait for the host event
72  std::unique_lock<std::mutex> lock(MMutex);
73  cv.wait(lock, [this] { return MState == HES_Complete; });
74  }
75 
76  // Wait for connected events(e.g. streams prints)
77  for (const EventImplPtr &Event : MPostCompleteEvents)
78  Event->wait(Event);
79 }
80 
81 void event_impl::setComplete() {
82  if (MHostEvent || !MEvent) {
83  std::unique_lock<std::mutex> lock(MMutex);
84 #ifndef NDEBUG
85  int Expected = HES_NotComplete;
86  int Desired = HES_Complete;
87 
88  bool Succeeded = MState.compare_exchange_strong(Expected, Desired);
89 
90  assert(Succeeded && "Unexpected state of event");
91 #else
92  MState.store(static_cast<int>(HES_Complete));
93 #endif
94  cv.notify_all();
95  return;
96  }
97 
98  assert(false && "setComplete is not supported for non-host event");
99 }
100 
101 const RT::PiEvent &event_impl::getHandleRef() const { return MEvent; }
102 RT::PiEvent &event_impl::getHandleRef() { return MEvent; }
103 
104 const ContextImplPtr &event_impl::getContextImpl() {
105  ensureContextInitialized();
106  return MContext;
107 }
108 
110  ensureContextInitialized();
111  return MContext->getPlugin();
112 }
113 
114 void event_impl::setStateIncomplete() { MState = HES_NotComplete; }
115 
116 void event_impl::setContextImpl(const ContextImplPtr &Context) {
117  MHostEvent = Context->is_host();
118  MContext = Context;
119  MIsContextInitialized = true;
120 }
121 
122 event_impl::event_impl(RT::PiEvent Event, const context &SyclContext)
123  : MIsContextInitialized(true), MEvent(Event),
124  MContext(detail::getSyclObjImpl(SyclContext)), MHostEvent(false),
125  MIsFlushed(true), MState(HES_Complete) {
126 
127  if (MContext->is_host()) {
128  throw sycl::invalid_parameter_error(
129  "The syclContext must match the OpenCL context associated with the "
130  "clEvent.",
131  PI_ERROR_INVALID_CONTEXT);
132  }
133 
134  RT::PiContext TempContext;
136  sizeof(RT::PiContext),
137  &TempContext, nullptr);
138  if (MContext->getHandleRef() != TempContext) {
139  throw sycl::invalid_parameter_error(
140  "The syclContext must match the OpenCL context associated with the "
141  "clEvent.",
142  PI_ERROR_INVALID_CONTEXT);
143  }
144 }
145 
147  : MQueue{Queue}, MIsProfilingEnabled{Queue->is_host() ||
148  Queue->MIsProfilingEnabled} {
149  this->setContextImpl(Queue->getContextImplPtr());
150 
151  if (Queue->is_host()) {
152  MState.store(HES_NotComplete);
153 
154  if (Queue->has_property<property::queue::enable_profiling>()) {
156  if (!MHostProfilingInfo)
157  throw runtime_error("Out of host memory", PI_ERROR_OUT_OF_HOST_MEMORY);
158  }
159  return;
160  }
161  MState.store(HES_Complete);
162 }
163 
164 void *event_impl::instrumentationProlog(std::string &Name, int32_t StreamID,
165  uint64_t &IId) const {
166  void *TraceEvent = nullptr;
167 #ifdef XPTI_ENABLE_INSTRUMENTATION
168  if (!xptiTraceEnabled())
169  return TraceEvent;
170  // Use a thread-safe counter to get a unique instance ID for the wait() on the
171  // event
172  static std::atomic<uint64_t> InstanceID = {1};
173  xpti::trace_event_data_t *WaitEvent = nullptr;
174 
175  // Create a string with the event address so it
176  // can be associated with other debug data
177  xpti::utils::StringHelper SH;
178  Name = SH.nameWithAddress<RT::PiEvent>("event.wait", MEvent);
179 
180  // We can emit the wait associated with the graph if the
181  // event does not have a command object or associated with
182  // the command object, if it exists
183  if (MCommand) {
184  Command *Cmd = (Command *)MCommand;
185  WaitEvent = Cmd->MTraceEvent ? static_cast<xpti_td *>(Cmd->MTraceEvent)
186  : GSYCLGraphEvent;
187  } else
188  WaitEvent = GSYCLGraphEvent;
189 
190  // Record the current instance ID for use by Epilog
191  IId = InstanceID++;
192  xptiNotifySubscribers(StreamID, xpti::trace_wait_begin, nullptr, WaitEvent,
193  IId, static_cast<const void *>(Name.c_str()));
194  TraceEvent = (void *)WaitEvent;
195 #endif
196  return TraceEvent;
197 }
198 
199 void event_impl::instrumentationEpilog(void *TelemetryEvent,
200  const std::string &Name,
201  int32_t StreamID, uint64_t IId) const {
202 #ifdef XPTI_ENABLE_INSTRUMENTATION
203  if (!(xptiTraceEnabled() && TelemetryEvent))
204  return;
205  // Close the wait() scope
206  xpti::trace_event_data_t *TraceEvent =
207  (xpti::trace_event_data_t *)TelemetryEvent;
208  xptiNotifySubscribers(StreamID, xpti::trace_wait_end, nullptr, TraceEvent,
209  IId, static_cast<const void *>(Name.c_str()));
210 #endif
211 }
212 
213 void event_impl::wait(std::shared_ptr<sycl::detail::event_impl> Self) {
214  if (MState == HES_Discarded)
215  throw sycl::exception(make_error_code(errc::invalid),
216  "wait method cannot be used for a discarded event.");
217 
218 #ifdef XPTI_ENABLE_INSTRUMENTATION
219  void *TelemetryEvent = nullptr;
220  uint64_t IId;
221  std::string Name;
222  int32_t StreamID = xptiRegisterStream(SYCL_STREAM_NAME);
223  TelemetryEvent = instrumentationProlog(Name, StreamID, IId);
224 #endif
225 
226  if (MEvent)
227  // presence of MEvent means the command has been enqueued, so no need to
228  // go via the slow path event waiting in the scheduler
229  waitInternal();
230  else if (MCommand)
232  cleanupCommand(std::move(Self));
233 
234 #ifdef XPTI_ENABLE_INSTRUMENTATION
235  instrumentationEpilog(TelemetryEvent, Name, StreamID, IId);
236 #endif
237 }
238 
240  std::shared_ptr<sycl::detail::event_impl> Self) {
241  wait(Self);
242 
243  if (QueueImplPtr SubmittedQueue = MSubmittedQueue.lock())
244  SubmittedQueue->throw_asynchronous();
245 }
246 
248  std::shared_ptr<sycl::detail::event_impl> Self) const {
251 }
252 
254  std::weak_ptr<queue_impl> EmptyPtr;
255 
256  if (!EmptyPtr.owner_before(MQueue) && !MQueue.owner_before(EmptyPtr)) {
257  throw sycl::exception(make_error_code(sycl::errc::invalid),
258  "Profiling information is unavailable as the event "
259  "has no associated queue.");
260  }
261  if (!MIsProfilingEnabled) {
262  throw sycl::exception(
263  make_error_code(sycl::errc::invalid),
264  "Profiling information is unavailable as the queue associated with "
265  "the event does not have the 'enable_profiling' property.");
266  }
267 }
268 
269 template <>
270 uint64_t
271 event_impl::get_profiling_info<info::event_profiling::command_submit>() {
272  checkProfilingPreconditions();
273  if (!MHostEvent) {
274  if (MEvent)
275  return get_event_profiling_info<info::event_profiling::command_submit>(
276  this->getHandleRef(), this->getPlugin());
277  return 0;
278  }
279  if (!MHostProfilingInfo)
280  throw invalid_object_error("Profiling info is not available.",
281  PI_ERROR_PROFILING_INFO_NOT_AVAILABLE);
282  return MHostProfilingInfo->getStartTime();
283 }
284 
285 template <>
286 uint64_t
287 event_impl::get_profiling_info<info::event_profiling::command_start>() {
288  checkProfilingPreconditions();
289  if (!MHostEvent) {
290  if (MEvent)
291  return get_event_profiling_info<info::event_profiling::command_start>(
292  this->getHandleRef(), this->getPlugin());
293  return 0;
294  }
295  if (!MHostProfilingInfo)
296  throw invalid_object_error("Profiling info is not available.",
297  PI_ERROR_PROFILING_INFO_NOT_AVAILABLE);
298  return MHostProfilingInfo->getStartTime();
299 }
300 
301 template <>
302 uint64_t event_impl::get_profiling_info<info::event_profiling::command_end>() {
303  checkProfilingPreconditions();
304  if (!MHostEvent) {
305  if (MEvent)
306  return get_event_profiling_info<info::event_profiling::command_end>(
307  this->getHandleRef(), this->getPlugin());
308  return 0;
309  }
310  if (!MHostProfilingInfo)
311  throw invalid_object_error("Profiling info is not available.",
312  PI_ERROR_PROFILING_INFO_NOT_AVAILABLE);
313  return MHostProfilingInfo->getEndTime();
314 }
315 
316 template <> uint32_t event_impl::get_info<info::event::reference_count>() {
317  if (!MHostEvent && MEvent) {
318  return get_event_info<info::event::reference_count>(this->getHandleRef(),
319  this->getPlugin());
320  }
321  return 0;
322 }
323 
324 template <>
326 event_impl::get_info<info::event::command_execution_status>() {
327  if (MState == HES_Discarded)
329 
330  if (!MHostEvent) {
331  // Command is enqueued and PiEvent is ready
332  if (MEvent)
333  return get_event_info<info::event::command_execution_status>(
334  this->getHandleRef(), this->getPlugin());
335  // Command is blocked and not enqueued, PiEvent is not assigned yet
336  else if (MCommand)
337  return sycl::info::event_command_status::submitted;
338  }
339 
340  return MHostEvent && MState.load() != HES_Complete
341  ? sycl::info::event_command_status::submitted
343 }
344 
345 static uint64_t getTimestamp() {
346  auto TimeStamp = std::chrono::high_resolution_clock::now().time_since_epoch();
347  return std::chrono::duration_cast<std::chrono::nanoseconds>(TimeStamp)
348  .count();
349 }
350 
351 void HostProfilingInfo::start() { StartTime = getTimestamp(); }
352 
353 void HostProfilingInfo::end() { EndTime = getTimestamp(); }
354 
357 
358  auto Plugin = getPlugin();
359  if (!MIsInitialized) {
360  MIsInitialized = true;
361  auto TempContext = MContext.get()->getHandleRef();
362  Plugin.call<PiApiKind::piEventCreate>(TempContext, &MEvent);
363  }
364  if (Plugin.getBackend() == backend::opencl)
365  Plugin.call<PiApiKind::piEventRetain>(getHandleRef());
366  pi_native_handle Handle;
367  Plugin.call<PiApiKind::piextEventGetNativeHandle>(getHandleRef(), &Handle);
368  return Handle;
369 }
370 
371 std::vector<EventImplPtr> event_impl::getWaitList() {
372  if (MState == HES_Discarded)
373  throw sycl::exception(
375  "get_wait_list() cannot be used for a discarded event.");
376 
377  std::lock_guard<std::mutex> Lock(MMutex);
378 
379  std::vector<EventImplPtr> Result;
380  Result.reserve(MPreparedDepsEvents.size() + MPreparedHostDepsEvents.size());
381  Result.insert(Result.end(), MPreparedDepsEvents.begin(),
382  MPreparedDepsEvents.end());
383  Result.insert(Result.end(), MPreparedHostDepsEvents.begin(),
385 
386  return Result;
387 }
388 
389 void event_impl::flushIfNeeded(const QueueImplPtr &UserQueue) {
390  // Some events might not have a native handle underneath even at this point,
391  // e.g. those produced by memset with 0 size (no PI call is made).
392  if (MIsFlushed || !MEvent)
393  return;
394 
395  QueueImplPtr Queue = MQueue.lock();
396  // If the queue has been released, all of the commands have already been
397  // implicitly flushed by piQueueRelease.
398  if (!Queue) {
399  MIsFlushed = true;
400  return;
401  }
402  if (Queue == UserQueue)
403  return;
404 
405  // Check if the task for this event has already been submitted.
409  nullptr);
410  if (Status == PI_EVENT_QUEUED) {
411  getPlugin().call<PiApiKind::piQueueFlush>(Queue->getHandleRef());
412  }
413  MIsFlushed = true;
414 }
415 
417  std::lock_guard<std::mutex> Lock(MMutex);
418  MPreparedDepsEvents.clear();
419  MPreparedHostDepsEvents.clear();
420 }
421 
423  std::lock_guard<std::mutex> Lock(MMutex);
424  for (auto &Event : MPreparedDepsEvents) {
425  Event->cleanupDependencyEvents();
426  }
427  for (auto &Event : MPreparedHostDepsEvents) {
428  Event->cleanupDependencyEvents();
429  }
430 }
431 
432 } // namespace detail
433 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
434 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:41
The Command class represents some action that needs to be performed on one or more memory objects.
Definition: commands.hpp:95
void * MTraceEvent
The event for node_create and task_begin.
Definition: commands.hpp:296
Profiling info for the host execution.
void end()
Measures event's end time.
Definition: event_impl.cpp:353
void start()
Measures event's start time.
Definition: event_impl.cpp:351
void cleanupFinishedCommands(const EventImplPtr &FinishedEvent)
Removes finished non-leaf non-alloca commands from the subgraph (assuming that all its commands have ...
Definition: scheduler.cpp:231
void waitForEvent(const EventImplPtr &Event)
Waits for the event.
Definition: scheduler.cpp:211
static Scheduler & getInstance()
Definition: scheduler.cpp:207
void checkProfilingPreconditions() const
Definition: event_impl.cpp:253
std::vector< EventImplPtr > getWaitList()
Returns vector of event_impl that this event_impl depends on.
Definition: event_impl.cpp:371
void * instrumentationProlog(std::string &Name, int32_t StreamID, uint64_t &instance_id) const
Definition: event_impl.cpp:164
void cleanDepEventsThroughOneLevel()
Cleans dependencies of this event's dependencies.
Definition: event_impl.cpp:422
pi_native_handle getNative()
Gets the native handle of the SYCL event.
Definition: event_impl.cpp:355
RT::PiEvent & getHandleRef()
Returns raw interoperability event handle.
Definition: event_impl.cpp:102
std::unique_ptr< HostProfilingInfo > MHostProfilingInfo
Definition: event_impl.hpp:268
void setContextImpl(const ContextImplPtr &Context)
Associate event with the context.
Definition: event_impl.cpp:116
void flushIfNeeded(const QueueImplPtr &UserQueue)
Performs a flush on the queue associated with this event if the user queue is different and the task ...
Definition: event_impl.cpp:389
std::weak_ptr< queue_impl > MSubmittedQueue
Definition: event_impl.hpp:274
std::atomic< int > MState
Definition: event_impl.hpp:289
void instrumentationEpilog(void *TelementryEvent, const std::string &Name, int32_t StreamID, uint64_t IId) const
Definition: event_impl.cpp:199
event_impl(std::optional< HostEventState > State=HES_Complete)
Constructs a ready SYCL event.
Definition: event_impl.hpp:49
std::vector< EventImplPtr > MPreparedHostDepsEvents
Definition: event_impl.hpp:278
void waitInternal()
Waits for the event with respect to device type.
Definition: event_impl.cpp:61
std::vector< EventImplPtr > MPreparedDepsEvents
Dependency events prepared for waiting by backend.
Definition: event_impl.hpp:277
void cleanupDependencyEvents()
Cleans dependencies of this event_impl.
Definition: event_impl.cpp:416
void wait_and_throw(std::shared_ptr< sycl::detail::event_impl > Self)
Waits for the event.
Definition: event_impl.cpp:239
void wait(std::shared_ptr< sycl::detail::event_impl > Self)
Waits for the event.
Definition: event_impl.cpp:213
void cleanupCommand(std::shared_ptr< sycl::detail::event_impl > Self) const
Clean up the command associated with the event.
Definition: event_impl.cpp:247
std::atomic< bool > MIsFlushed
Indicates that the task associated with this event has been submitted by the queue to the device.
Definition: event_impl.hpp:284
std::weak_ptr< queue_impl > MQueue
Definition: event_impl.hpp:270
The plugin class provides a unified interface to the underlying low-level runtimes for the device-agn...
Definition: plugin.hpp:90
void call(ArgsT... Args) const
Calls the API, traces the call, checks the result.
Definition: plugin.hpp:217
The SYCL device class encapsulates a single SYCL device on which kernels may be executed.
Definition: device.hpp:49
#define __SYCL_INLINE_VER_NAMESPACE(X)
::pi_event PiEvent
Definition: pi.hpp:121
const plugin & getPlugin()
Definition: pi.cpp:509
::pi_context PiContext
Definition: pi.hpp:114
static const plugin & getPlugin(backend Backend)
Definition: backend.cpp:32
constexpr const char * SYCL_STREAM_NAME
static uint64_t getTimestamp()
Definition: event_impl.cpp:345
std::shared_ptr< sycl::detail::context_impl > ContextImplPtr
Definition: event_impl.hpp:30
decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject)
Definition: common.hpp:248
std::shared_ptr< event_impl > EventImplPtr
Definition: cg.hpp:42
std::shared_ptr< sycl::detail::queue_impl > QueueImplPtr
Definition: event_impl.hpp:32
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:91
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
int32_t pi_int32
Definition: pi.h:106
uintptr_t pi_native_handle
Definition: pi.h:111
pi_result piextEventGetNativeHandle(pi_event event, pi_native_handle *nativeHandle)
Gets the native handle of a PI event object.
pi_result piEventGetInfo(pi_event event, pi_event_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
@ PI_EVENT_INFO_COMMAND_EXECUTION_STATUS
Definition: pi.h:383
@ PI_EVENT_INFO_CONTEXT
Definition: pi.h:381
pi_result piEventsWait(pi_uint32 num_events, const pi_event *event_list)
pi_result piQueueFlush(pi_queue command_queue)
pi_result piEventRelease(pi_event event)
_pi_event_status
Definition: pi.h:126
@ PI_EVENT_QUEUED
Definition: pi.h:130
pi_result piEventRetain(pi_event event)
decltype(piEventCreate) piEventCreate