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 <CL/sycl/context.hpp>
10 #include <detail/event_impl.hpp>
11 #include <detail/event_info.hpp>
12 #include <detail/plugin.hpp>
13 #include <detail/queue_impl.hpp>
15 
16 #include "detail/config.hpp"
17 
18 #include <chrono>
19 
20 #ifdef XPTI_ENABLE_INSTRUMENTATION
21 #include "xpti/xpti_trace_framework.hpp"
22 #include <atomic>
23 #include <detail/xpti_registry.hpp>
24 #include <sstream>
25 #endif
26 
28 namespace sycl {
29 namespace detail {
30 #ifdef XPTI_ENABLE_INSTRUMENTATION
31 extern xpti::trace_event_data_t *GSYCLGraphEvent;
32 #endif
33 
34 // Threat all devices that don't support interoperability as host devices to
35 // avoid attempts to call method get on such events.
36 bool event_impl::is_host() const { return MHostEvent || !MOpenCLInterop; }
37 
38 cl_event event_impl::get() const {
39  if (!MOpenCLInterop) {
40  throw invalid_object_error(
41  "This instance of event doesn't support OpenCL interoperability.",
43  }
45  return pi::cast<cl_event>(MEvent);
46 }
47 
48 event_impl::~event_impl() {
49  if (MEvent)
51 }
52 
53 void event_impl::waitInternal() const {
54  if (!MHostEvent && MEvent) {
55  getPlugin().call<PiApiKind::piEventsWait>(1, &MEvent);
56  return;
57  }
58 
59  if (MState == HES_Discarded)
60  throw sycl::exception(
61  make_error_code(errc::invalid),
62  "waitInternal method cannot be used for a discarded event.");
63 
64  while (MState != HES_Complete)
65  ;
66 }
67 
68 void event_impl::setComplete() {
69  if (MHostEvent || !MEvent) {
70 #ifndef NDEBUG
71  int Expected = HES_NotComplete;
72  int Desired = HES_Complete;
73 
74  bool Succeeded = MState.compare_exchange_strong(Expected, Desired);
75 
76  assert(Succeeded && "Unexpected state of event");
77 #else
78  MState.store(static_cast<int>(HES_Complete));
79 #endif
80  return;
81  }
82 
83  assert(false && "setComplete is not supported for non-host event");
84 }
85 
86 const RT::PiEvent &event_impl::getHandleRef() const { return MEvent; }
87 RT::PiEvent &event_impl::getHandleRef() { return MEvent; }
88 
89 const ContextImplPtr &event_impl::getContextImpl() { return MContext; }
90 
91 const plugin &event_impl::getPlugin() const { return MContext->getPlugin(); }
92 
93 void event_impl::setContextImpl(const ContextImplPtr &Context) {
94  MHostEvent = Context->is_host();
95  MOpenCLInterop = !MHostEvent;
96  MContext = Context;
97 
98  MState = HES_NotComplete;
99 }
100 
101 event_impl::event_impl(HostEventState State)
102  : MIsInitialized(false), MIsFlushed(true), MState(State) {}
103 
104 event_impl::event_impl(RT::PiEvent Event, const context &SyclContext)
105  : MEvent(Event), MContext(detail::getSyclObjImpl(SyclContext)),
106  MOpenCLInterop(true), MHostEvent(false), MIsFlushed(true),
107  MState(HES_Complete) {
108 
109  if (MContext->is_host()) {
110  throw cl::sycl::invalid_parameter_error(
111  "The syclContext must match the OpenCL context associated with the "
112  "clEvent.",
114  }
115 
116  RT::PiContext TempContext;
118  sizeof(RT::PiContext),
119  &TempContext, nullptr);
120  if (MContext->getHandleRef() != TempContext) {
121  throw cl::sycl::invalid_parameter_error(
122  "The syclContext must match the OpenCL context associated with the "
123  "clEvent.",
125  }
126 
128 }
129 
131  : MQueue{Queue}, MIsProfilingEnabled{Queue->is_host() ||
132  Queue->MIsProfilingEnabled} {
133  if (Queue->is_host()) {
134  MState.store(HES_NotComplete);
135 
136  if (Queue->has_property<property::queue::enable_profiling>()) {
137  MHostProfilingInfo.reset(new HostProfilingInfo());
138  if (!MHostProfilingInfo)
139  throw runtime_error("Out of host memory", PI_OUT_OF_HOST_MEMORY);
140  }
141  return;
142  }
143  MState.store(HES_Complete);
144 }
145 
146 void *event_impl::instrumentationProlog(std::string &Name, int32_t StreamID,
147  uint64_t &IId) const {
148  void *TraceEvent = nullptr;
149 #ifdef XPTI_ENABLE_INSTRUMENTATION
150  if (!xptiTraceEnabled())
151  return TraceEvent;
152  // Use a thread-safe counter to get a unique instance ID for the wait() on the
153  // event
154  static std::atomic<uint64_t> InstanceID = {1};
155  xpti::trace_event_data_t *WaitEvent = nullptr;
156 
157  // Create a string with the event address so it
158  // can be associated with other debug data
159  xpti::utils::StringHelper SH;
160  Name = SH.nameWithAddress<RT::PiEvent>("event.wait", MEvent);
161 
162  // We can emit the wait associated with the graph if the
163  // event does not have a command object or associated with
164  // the command object, if it exists
165  if (MCommand) {
166  Command *Cmd = (Command *)MCommand;
167  WaitEvent = Cmd->MTraceEvent ? static_cast<xpti_td *>(Cmd->MTraceEvent)
168  : GSYCLGraphEvent;
169  } else
170  WaitEvent = GSYCLGraphEvent;
171 
172  // Record the current instance ID for use by Epilog
173  IId = InstanceID++;
174  xptiNotifySubscribers(StreamID, xpti::trace_wait_begin, nullptr, WaitEvent,
175  IId, static_cast<const void *>(Name.c_str()));
176  TraceEvent = (void *)WaitEvent;
177 #endif
178  return TraceEvent;
179 }
180 
181 void event_impl::instrumentationEpilog(void *TelemetryEvent,
182  const std::string &Name,
183  int32_t StreamID, uint64_t IId) const {
184 #ifdef XPTI_ENABLE_INSTRUMENTATION
185  if (!(xptiTraceEnabled() && TelemetryEvent))
186  return;
187  // Close the wait() scope
188  xpti::trace_event_data_t *TraceEvent =
189  (xpti::trace_event_data_t *)TelemetryEvent;
190  xptiNotifySubscribers(StreamID, xpti::trace_wait_end, nullptr, TraceEvent,
191  IId, static_cast<const void *>(Name.c_str()));
192 #endif
193 }
194 
196  std::shared_ptr<cl::sycl::detail::event_impl> Self) const {
197  if (MState == HES_Discarded)
199  "wait method cannot be used for a discarded event.");
200 
201 #ifdef XPTI_ENABLE_INSTRUMENTATION
202  void *TelemetryEvent = nullptr;
203  uint64_t IId;
204  std::string Name;
205  int32_t StreamID = xptiRegisterStream(SYCL_STREAM_NAME);
206  TelemetryEvent = instrumentationProlog(Name, StreamID, IId);
207 #endif
208 
209  if (MEvent)
210  // presence of MEvent means the command has been enqueued, so no need to
211  // go via the slow path event waiting in the scheduler
212  waitInternal();
213  else if (MCommand)
215  cleanupCommand(std::move(Self));
216 
217 #ifdef XPTI_ENABLE_INSTRUMENTATION
218  instrumentationEpilog(TelemetryEvent, Name, StreamID, IId);
219 #endif
220 }
221 
223  std::shared_ptr<cl::sycl::detail::event_impl> Self) {
225 
226  QueueImplPtr submittedQueue = nullptr;
227  {
228  Scheduler::ReadLockT Lock(Sched.MGraphLock);
229  Command *Cmd = static_cast<Command *>(Self->getCommand());
230  if (Cmd)
231  submittedQueue = Cmd->getSubmittedQueue();
232  }
233  wait(Self);
234 
235  {
236  Scheduler::ReadLockT Lock(Sched.MGraphLock);
237  for (auto &EventImpl : getWaitList()) {
238  Command *Cmd = (Command *)EventImpl->getCommand();
239  if (Cmd)
240  Cmd->getSubmittedQueue()->throw_asynchronous();
241  }
242  }
243  if (submittedQueue)
244  submittedQueue->throw_asynchronous();
245 }
246 
248  std::shared_ptr<cl::sycl::detail::event_impl> Self) const {
251 }
252 
253 void event_impl::checkProfilingPreconditions() const {
254  if (!MIsProfilingEnabled) {
256  "get_profiling_info() can't be used without set "
257  "'enable_profiling' queue property");
258  }
259 }
260 
261 template <>
262 cl_ulong
263 event_impl::get_profiling_info<info::event_profiling::command_submit>() const {
264  checkProfilingPreconditions();
265  if (!MHostEvent) {
266  if (MEvent)
268  info::event_profiling::command_submit>::get(this->getHandleRef(),
269  this->getPlugin());
270  return 0;
271  }
272  if (!MHostProfilingInfo)
273  throw invalid_object_error("Profiling info is not available.",
275  return MHostProfilingInfo->getStartTime();
276 }
277 
278 template <>
279 cl_ulong
280 event_impl::get_profiling_info<info::event_profiling::command_start>() const {
281  checkProfilingPreconditions();
282  if (!MHostEvent) {
283  if (MEvent)
285  info::event_profiling::command_start>::get(this->getHandleRef(),
286  this->getPlugin());
287  return 0;
288  }
289  if (!MHostProfilingInfo)
290  throw invalid_object_error("Profiling info is not available.",
292  return MHostProfilingInfo->getStartTime();
293 }
294 
295 template <>
296 cl_ulong
297 event_impl::get_profiling_info<info::event_profiling::command_end>() const {
298  checkProfilingPreconditions();
299  if (!MHostEvent) {
300  if (MEvent)
302  this->getHandleRef(), this->getPlugin());
303  return 0;
304  }
305  if (!MHostProfilingInfo)
306  throw invalid_object_error("Profiling info is not available.",
308  return MHostProfilingInfo->getEndTime();
309 }
310 
311 template <> cl_uint event_impl::get_info<info::event::reference_count>() const {
312  if (!MHostEvent && MEvent) {
314  this->getHandleRef(), this->getPlugin());
315  }
316  return 0;
317 }
318 
319 template <>
321 event_impl::get_info<info::event::command_execution_status>() const {
322  if (MState == HES_Discarded)
324 
325  if (!MHostEvent && MEvent) {
327  this->getHandleRef(), this->getPlugin());
328  }
329  return MHostEvent && MState.load() != HES_Complete
332 }
333 
334 static uint64_t getTimestamp() {
335  auto TimeStamp = std::chrono::high_resolution_clock::now().time_since_epoch();
336  return std::chrono::duration_cast<std::chrono::nanoseconds>(TimeStamp)
337  .count();
338 }
339 
340 void HostProfilingInfo::start() { StartTime = getTimestamp(); }
341 
342 void HostProfilingInfo::end() { EndTime = getTimestamp(); }
343 
345  if (!MContext) {
346  static context SyclContext;
347  MContext = getSyclObjImpl(SyclContext);
348  MHostEvent = MContext->is_host();
349  MOpenCLInterop = !MHostEvent;
350  }
351  auto Plugin = getPlugin();
352  if (!MIsInitialized) {
353  MIsInitialized = true;
354  auto TempContext = MContext.get()->getHandleRef();
355  Plugin.call<PiApiKind::piEventCreate>(TempContext, &MEvent);
356  }
357  if (Plugin.getBackend() == backend::opencl)
358  Plugin.call<PiApiKind::piEventRetain>(getHandleRef());
359  pi_native_handle Handle;
360  Plugin.call<PiApiKind::piextEventGetNativeHandle>(getHandleRef(), &Handle);
361  return Handle;
362 }
363 
364 std::vector<EventImplPtr> event_impl::getWaitList() {
365  if (MState == HES_Discarded)
366  throw sycl::exception(
368  "get_wait_list() cannot be used for a discarded event.");
369 
370  std::lock_guard<std::mutex> Lock(MMutex);
371 
372  std::vector<EventImplPtr> Result;
373  Result.reserve(MPreparedDepsEvents.size() + MPreparedHostDepsEvents.size());
374  Result.insert(Result.end(), MPreparedDepsEvents.begin(),
375  MPreparedDepsEvents.end());
376  Result.insert(Result.end(), MPreparedHostDepsEvents.begin(),
377  MPreparedHostDepsEvents.end());
378 
379  return Result;
380 }
381 
382 void event_impl::flushIfNeeded(const QueueImplPtr &UserQueue) {
383  if (MIsFlushed)
384  return;
385 
386  QueueImplPtr Queue = MQueue.lock();
387  // If the queue has been released, all of the commands have already been
388  // implicitly flushed by piQueueRelease.
389  if (!Queue) {
390  MIsFlushed = true;
391  return;
392  }
393  if (Queue == UserQueue)
394  return;
395 
396  // Check if the task for this event has already been submitted.
397  assert(MEvent != nullptr);
400  MEvent, PI_EVENT_INFO_COMMAND_EXECUTION_STATUS, sizeof(pi_int32), &Status,
401  nullptr);
402  if (Status == PI_EVENT_QUEUED) {
403  getPlugin().call<PiApiKind::piQueueFlush>(Queue->getHandleRef());
404  }
405  MIsFlushed = true;
406 }
407 
409  std::lock_guard<std::mutex> Lock(MMutex);
410  MPreparedDepsEvents.clear();
411  MPreparedHostDepsEvents.clear();
412 }
413 
415  std::lock_guard<std::mutex> Lock(MMutex);
416  for (auto &Event : MPreparedDepsEvents) {
417  Event->cleanupDependencyEvents();
418  }
419  for (auto &Event : MPreparedHostDepsEvents) {
420  Event->cleanupDependencyEvents();
421  }
422 }
423 
424 } // namespace detail
425 } // namespace sycl
426 } // __SYCL_INLINE_NAMESPACE(cl)
cl::sycl::detail::Scheduler
DPC++ graph scheduler class.
Definition: scheduler.hpp:358
piEventRelease
pi_result piEventRelease(pi_event event)
Definition: pi_esimd_emulator.cpp:1442
event_info.hpp
event_impl.hpp
cl::sycl::detail::pi::getPlugin
const plugin & getPlugin()
Definition: pi.cpp:511
cl::sycl::detail::ContextImplPtr
std::shared_ptr< detail::context_impl > ContextImplPtr
Definition: memory_manager.hpp:32
cl::sycl::detail::get_event_info::get
static RetType get(RT::PiEvent Event, const plugin &Plugin)
Definition: event_info.hpp:36
config.hpp
cl::sycl::detail::HostProfilingInfo
Profiling info for the host execution.
Definition: host_profiling_info.hpp:19
cl::sycl::backend::opencl
@ opencl
cl::sycl::detail::getPlugin
static const plugin & getPlugin(backend Backend)
Definition: backend.cpp:32
cl::sycl::detail::event_impl::HostEventState
HostEventState
Definition: event_impl.hpp:35
cl::sycl::detail::getTimestamp
static uint64_t getTimestamp()
Definition: event_impl.cpp:334
xpti_registry.hpp
PI_EVENT_INFO_CONTEXT
@ PI_EVENT_INFO_CONTEXT
Definition: pi.h:411
cl::sycl::detail::SYCLConfig
Definition: config.hpp:105
cl::sycl::detail::event_impl::cleanupDependencyEvents
void cleanupDependencyEvents()
Cleans dependencies of this event_impl.
Definition: event_impl.cpp:408
PI_PROFILING_INFO_NOT_AVAILABLE
@ PI_PROFILING_INFO_NOT_AVAILABLE
Definition: pi.h:110
context.hpp
cl::sycl::make_error_code
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:121
cl::sycl::detail::event_impl::getHandleRef
RT::PiEvent & getHandleRef()
Returns raw interoperability event handle.
Definition: event_impl.cpp:86
cl::sycl::detail::event_impl::HES_Discarded
@ HES_Discarded
Definition: event_impl.hpp:38
cl::sycl::detail::get_event_profiling_info
Definition: event_info.hpp:20
sycl
Definition: invoke_simd.hpp:68
plugin.hpp
cl::sycl::info::event_profiling::command_start
@ command_start
queue_impl.hpp
cl::sycl::detail::pi::PiContext
::pi_context PiContext
Definition: pi.hpp:106
cl::sycl::detail::SYCL_STREAM_NAME
constexpr const char * SYCL_STREAM_NAME
Definition: xpti_registry.hpp:28
scheduler.hpp
PI_INVALID_EVENT
@ PI_INVALID_EVENT
Definition: pi.h:104
cl::sycl::detail::Command::getSubmittedQueue
const QueueImplPtr & getSubmittedQueue() const
Definition: commands.hpp:151
cl::sycl::detail::event_impl::cleanupCommand
void cleanupCommand(std::shared_ptr< cl::sycl::detail::event_impl > Self) const
Clean up the command associated with the event.
Definition: event_impl.cpp:247
piEventsWait
pi_result piEventsWait(pi_uint32 num_events, const pi_event *event_list)
Definition: pi_esimd_emulator.cpp:1407
cl::sycl::info::event_command_status
event_command_status
Definition: info_desc.hpp:294
cl::sycl::detail::Scheduler::getInstance
static Scheduler & getInstance()
Definition: scheduler.cpp:209
cl::sycl::info::event_profiling::command_submit
@ command_submit
cl::sycl::property::queue::enable_profiling
Definition: queue_properties.hpp:19
_pi_event_status
_pi_event_status
Definition: pi.h:134
cl::sycl::detail::HostProfilingInfo::end
void end()
Measures event's end time.
Definition: event_impl.cpp:342
cl::sycl::detail::plugin::call
void call(ArgsT... Args) const
Calls the API, traces the call, checks the result.
Definition: plugin.hpp:217
piEventCreate
decltype(piEventCreate) piEventCreate
Definition: pi_level_zero.cpp:1823
cl::sycl::detail::Command
The Command class represents some action that needs to be performed on one or more memory objects.
Definition: commands.hpp:95
cl
We provide new interfaces for matrix muliply in this patch:
Definition: access.hpp:13
piextEventGetNativeHandle
pi_result piextEventGetNativeHandle(pi_event event, pi_native_handle *nativeHandle)
Gets the native handle of a PI event object.
Definition: pi_esimd_emulator.cpp:1463
cl::sycl::detail::event_impl::waitInternal
void waitInternal() const
Waits for the event with respect to device type.
Definition: event_impl.cpp:53
cl::sycl::detail::plugin
The plugin class provides a unified interface to the underlying low-level runtimes for the device-agn...
Definition: plugin.hpp:90
cl::sycl::info::event_command_status::submitted
@ submitted
cl::sycl::cl_ulong
std::uint64_t cl_ulong
Definition: aliases.hpp:85
cl::sycl::detail::event_impl::wait
void wait(std::shared_ptr< cl::sycl::detail::event_impl > Self) const
Waits for the event.
Definition: event_impl.cpp:195
pi_native_handle
uintptr_t pi_native_handle
Definition: pi.h:76
cl::sycl::detail::Scheduler::waitForEvent
void waitForEvent(EventImplPtr Event)
Waits for the event.
Definition: scheduler.cpp:213
PI_INVALID_CONTEXT
@ PI_INVALID_CONTEXT
Definition: pi.h:92
cl::sycl::detail::QueueImplPtr
std::shared_ptr< detail::queue_impl > QueueImplPtr
Definition: memory_manager.hpp:30
cl::sycl::info::event_command_status::ext_oneapi_unknown
@ ext_oneapi_unknown
std::get
constexpr tuple_element< I, tuple< Types... > >::type & get(cl::sycl::detail::tuple< Types... > &Arg) noexcept
Definition: tuple.hpp:199
PI_EVENT_QUEUED
@ PI_EVENT_QUEUED
Definition: pi.h:138
cl::sycl::detail::Scheduler::ReadLockT
std::shared_lock< RWLockT > ReadLockT
Definition: scheduler.hpp:453
cl::sycl::detail::event_impl::getPlugin
const plugin & getPlugin() const
Definition: event_impl.cpp:91
piEventGetInfo
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)
Definition: pi_esimd_emulator.cpp:1387
cl::sycl::detail::getSyclObjImpl
decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject)
Definition: common.hpp:198
cl::sycl::detail::HostProfilingInfo::start
void start()
Measures event's start time.
Definition: event_impl.cpp:340
piEventRetain
pi_result piEventRetain(pi_event event)
Definition: pi_esimd_emulator.cpp:1432
_pi_event
PI Event mapping to CUevent.
Definition: pi_cuda.hpp:458
cl::sycl::context
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:35
cl::sycl::info::event_command_status::complete
@ complete
cl::sycl::errc::invalid
@ invalid
cl::sycl::cl_uint
std::uint32_t cl_uint
Definition: aliases.hpp:83
PI_OUT_OF_HOST_MEMORY
@ PI_OUT_OF_HOST_MEMORY
Definition: pi.h:97
cl::sycl::detail::get_event_profiling_info::get
static RetType get(RT::PiEvent Event, const plugin &Plugin)
Definition: event_info.hpp:24
cl::sycl::detail::Scheduler::MGraphLock
RWLockT MGraphLock
Definition: scheduler.hpp:768
piQueueFlush
pi_result piQueueFlush(pi_queue command_queue)
Definition: pi_esimd_emulator.cpp:991
cl::sycl::detail::event_impl::getNative
pi_native_handle getNative() const
Gets the native handle of the SYCL event.
Definition: event_impl.cpp:344
cl::sycl::exception
Definition: exception.hpp:63
PI_EVENT_INFO_COMMAND_EXECUTION_STATUS
@ PI_EVENT_INFO_COMMAND_EXECUTION_STATUS
Definition: pi.h:413
cl::sycl::detail::event_impl::event_impl
event_impl(HostEventState State=HES_Complete)
Constructs a ready SYCL event.
Definition: event_impl.cpp:101
cl::sycl::detail::event_impl::wait_and_throw
void wait_and_throw(std::shared_ptr< cl::sycl::detail::event_impl > Self)
Waits for the event.
Definition: event_impl.cpp:222
cl::sycl::detail::event_impl::getWaitList
std::vector< EventImplPtr > getWaitList()
Returns vector of event_impl that this event_impl depends on.
Definition: event_impl.cpp:364
cl::sycl::detail::Scheduler::cleanupFinishedCommands
void cleanupFinishedCommands(EventImplPtr FinishedEvent)
Removes finished non-leaf non-alloca commands from the subgraph (assuming that all its commands have ...
Definition: scheduler.cpp:233
pi_int32
int32_t pi_int32
Definition: pi.h:71
cl::sycl::detail::event_impl::cleanDepEventsThroughOneLevel
void cleanDepEventsThroughOneLevel()
Cleans dependencies of this event's dependencies.
Definition: event_impl.cpp:414
_pi_context
PI context mapping to a CUDA context object.
Definition: pi_cuda.hpp:150
__SYCL_INLINE_NAMESPACE
#define __SYCL_INLINE_NAMESPACE(X)
Definition: defines_elementary.hpp:12
cl::sycl::detail::event_impl::flushIfNeeded
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:382