DPC++ Runtime
Runtime libraries for oneAPI DPC++
sycl_mem_obj_t.hpp
Go to the documentation of this file.
1 //==------------ sycl_mem_obj_t.hpp - SYCL standard header file ------------==//
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 #pragma once
10 
12 #include <sycl/detail/common.hpp>
13 #include <sycl/detail/export.hpp>
16 #include <sycl/detail/ur.hpp>
17 #include <sycl/event.hpp>
20 #include <sycl/property_list.hpp>
21 #include <sycl/range.hpp>
22 
23 #include <atomic>
24 #include <cstring>
25 #include <memory>
26 #include <mutex>
27 #include <type_traits>
28 
29 namespace sycl {
30 inline namespace _V1 {
31 namespace detail {
32 
33 // Forward declarations
34 class context_impl;
35 class event_impl;
36 class plugin;
37 
38 using ContextImplPtr = std::shared_ptr<context_impl>;
39 using EventImplPtr = std::shared_ptr<event_impl>;
40 
41 // The class serves as a base for all SYCL memory objects.
42 class SYCLMemObjT : public SYCLMemObjI {
43 
44  // The check for output iterator is commented out as it blocks set_final_data
45  // with void * argument to be used.
46  // TODO: Align these checks with the SYCL specification when the behaviour
47  // with void * is clarified.
48  template <typename T>
49  using EnableIfOutputPointerT = std::enable_if_t<
50  /*is_output_iterator<T>::value &&*/ std::is_pointer<T>::value>;
51 
52  template <typename T>
53  using EnableIfOutputIteratorT = std::enable_if_t<
54  /*is_output_iterator<T>::value &&*/ !std::is_pointer<T>::value>;
55 
56 public:
57  SYCLMemObjT(const size_t SizeInBytes, const property_list &Props,
58  std::unique_ptr<SYCLMemObjAllocator> Allocator)
59  : MAllocator(std::move(Allocator)), MProps(Props), MInteropEvent(nullptr),
60  MInteropContext(nullptr), MInteropMemObject(nullptr),
61  MOpenCLInterop(false), MHostPtrReadOnly(false), MNeedWriteBack(true),
62  MSizeInBytes(SizeInBytes), MUserPtr(nullptr), MShadowCopy(nullptr),
63  MUploadDataFunctor(nullptr), MSharedPtrStorage(nullptr),
64  MHostPtrProvided(false) {}
65 
66  SYCLMemObjT(const property_list &Props,
67  std::unique_ptr<SYCLMemObjAllocator> Allocator)
68  : SYCLMemObjT(/*SizeInBytes*/ 0, Props, std::move(Allocator)) {}
69 
70  SYCLMemObjT(ur_native_handle_t MemObject, const context &SyclContext,
71  const size_t SizeInBytes, event AvailableEvent,
72  std::unique_ptr<SYCLMemObjAllocator> Allocator);
73 
74  SYCLMemObjT(cl_mem MemObject, const context &SyclContext,
75  event AvailableEvent,
76  std::unique_ptr<SYCLMemObjAllocator> Allocator)
77  : SYCLMemObjT(ur::cast<ur_native_handle_t>(MemObject), SyclContext,
78  /*SizeInBytes*/ (size_t)0, AvailableEvent,
79  std::move(Allocator)) {}
80 
81  SYCLMemObjT(ur_native_handle_t MemObject, const context &SyclContext,
82  bool OwnNativeHandle, event AvailableEvent,
83  std::unique_ptr<SYCLMemObjAllocator> Allocator);
84 
85  SYCLMemObjT(ur_native_handle_t MemObject, const context &SyclContext,
86  bool OwnNativeHandle, event AvailableEvent,
87  std::unique_ptr<SYCLMemObjAllocator> Allocator,
88  ur_image_format_t Format, range<3> Range3WithOnes,
89  unsigned Dimensions, size_t ElementSize);
90 
91  virtual ~SYCLMemObjT() = default;
92 
93  const PluginPtr &getPlugin() const;
94 
95  size_t getSizeInBytes() const noexcept override { return MSizeInBytes; }
96  __SYCL2020_DEPRECATED("get_count() is deprecated, please use size() instead")
97  size_t get_count() const { return size(); }
98  size_t size() const noexcept {
99  size_t AllocatorValueSize = MAllocator->getValueSize();
100  return (getSizeInBytes() + AllocatorValueSize - 1) / AllocatorValueSize;
101  }
102 
103  template <typename propertyT> bool has_property() const noexcept {
104  return MProps.has_property<propertyT>();
105  }
106 
107  template <typename propertyT> propertyT get_property() const {
108  return MProps.get_property<propertyT>();
109  }
110 
111  void addOrReplaceAccessorProperties(const property_list &PropertyList) {
113  }
114 
117  }
118 
119  const std::unique_ptr<SYCLMemObjAllocator> &get_allocator_internal() const {
120  return MAllocator;
121  }
122 
123  void *allocateHostMem() override { return MAllocator->allocate(size()); }
124 
125  void releaseHostMem(void *Ptr) override {
126  if (Ptr)
127  MAllocator->deallocate(Ptr, size());
128  }
129 
130  void releaseMem(ContextImplPtr Context, void *MemAllocation) override;
131 
132  void *getUserPtr() const {
133  return MOpenCLInterop ? static_cast<void *>(MInteropMemObject) : MUserPtr;
134  }
135 
136  void set_write_back(bool NeedWriteBack) { MNeedWriteBack = NeedWriteBack; }
137 
138  void set_final_data(std::nullptr_t) { MUploadDataFunctor = nullptr; }
139 
141  MUploadDataFunctor = [this]() {
142  if (MSharedPtrStorage.use_count() > 1) {
143  void *FinalData = const_cast<void *>(MSharedPtrStorage.get());
144  updateHostMemory(FinalData);
145  }
146  };
147  MHostPtrProvided = true;
148  }
149 
151  const std::function<void(const std::function<void(void *const Ptr)> &)>
152  &FinalDataFunc) {
153 
154  auto UpdateFunc = [this](void *const Ptr) { updateHostMemory(Ptr); };
155  MUploadDataFunctor = [FinalDataFunc, UpdateFunc]() {
156  FinalDataFunc(UpdateFunc);
157  };
158  MHostPtrProvided = true;
159  }
160 
161 protected:
162  void updateHostMemory(void *const Ptr);
163 
164  // Update host with the latest data + notify scheduler that the memory object
165  // is going to die. After this method is finished no further operations with
166  // the memory object is allowed. This method is executed from child's
167  // destructor. This cannot be done in SYCLMemObjT's destructor as child's
168  // members must be alive.
169  void updateHostMemory();
170 
171 public:
172  bool useHostPtr() {
173  return has_property<property::buffer::use_host_ptr>() ||
174  has_property<property::image::use_host_ptr>();
175  }
176 
177  bool canReadHostPtr(void *HostPtr, const size_t RequiredAlign) {
178  bool Aligned =
179  (reinterpret_cast<std::uintptr_t>(HostPtr) % RequiredAlign) == 0;
180  return Aligned || useHostPtr();
181  }
182 
183  bool canReuseHostPtr(void *HostPtr, const size_t RequiredAlign) {
184  return !MHostPtrReadOnly && canReadHostPtr(HostPtr, RequiredAlign);
185  }
186 
187  void handleHostData(void *HostPtr, const size_t RequiredAlign) {
188  MHostPtrProvided = true;
189  if (!MHostPtrReadOnly && HostPtr) {
190  set_final_data([HostPtr](const std::function<void(void *const Ptr)> &F) {
191  F(HostPtr);
192  });
193  }
194 
195  if (HostPtr) {
196  if (canReuseHostPtr(HostPtr, RequiredAlign)) {
197  MUserPtr = HostPtr;
198  } else if (canReadHostPtr(HostPtr, RequiredAlign)) {
199  MUserPtr = HostPtr;
200  std::lock_guard<std::mutex> Lock(MCreateShadowCopyMtx);
201  MCreateShadowCopy = [this, RequiredAlign, HostPtr]() -> void {
202  setAlign(RequiredAlign);
205  std::memcpy(MUserPtr, HostPtr, MSizeInBytes);
206  };
207  } else {
208  setAlign(RequiredAlign);
211  std::memcpy(MUserPtr, HostPtr, MSizeInBytes);
212  }
213  }
214  }
215 
216  void handleHostData(const void *HostPtr, const size_t RequiredAlign) {
217  MHostPtrReadOnly = true;
218  handleHostData(const_cast<void *>(HostPtr), RequiredAlign);
219  }
220 
221  void handleHostData(const std::shared_ptr<void> &HostPtr,
222  const size_t RequiredAlign, bool IsConstPtr) {
223  MHostPtrProvided = true;
224  MSharedPtrStorage = HostPtr;
225  MHostPtrReadOnly = IsConstPtr;
226  if (HostPtr) {
227  if (!MHostPtrReadOnly)
229 
230  if (canReuseHostPtr(HostPtr.get(), RequiredAlign)) {
231  MUserPtr = HostPtr.get();
232  } else if (canReadHostPtr(HostPtr.get(), RequiredAlign)) {
233  MUserPtr = HostPtr.get();
234  std::lock_guard<std::mutex> Lock(MCreateShadowCopyMtx);
235  MCreateShadowCopy = [this, RequiredAlign, HostPtr]() -> void {
236  setAlign(RequiredAlign);
239  std::memcpy(MUserPtr, HostPtr.get(), MSizeInBytes);
240  };
241  } else {
242  setAlign(RequiredAlign);
245  std::memcpy(MUserPtr, HostPtr.get(), MSizeInBytes);
246  }
247  }
248  }
249 
250  void handleHostData(const std::function<void(void *)> &CopyFromInput,
251  const size_t RequiredAlign, bool IsConstPtr) {
252  MHostPtrReadOnly = IsConstPtr;
253  setAlign(RequiredAlign);
254  if (useHostPtr())
256  "Buffer constructor from a pair of iterator values does "
257  "not support use_host_ptr property.");
258 
259  setAlign(RequiredAlign);
262 
263  CopyFromInput(MUserPtr);
264  }
265 
266  void setAlign(size_t RequiredAlign) {
267  MAllocator->setAlignment(RequiredAlign);
268  }
269 
270  static size_t getBufSizeForContext(const ContextImplPtr &Context,
271  ur_native_handle_t MemObject);
272 
274 
275  void *allocateMem(ContextImplPtr Context, bool InitFromUserData,
276  void *HostPtr, ur_event_handle_t &InteropEvent) override {
277  (void)Context;
278  (void)InitFromUserData;
279  (void)HostPtr;
280  (void)InteropEvent;
281  throw exception(make_error_code(errc::runtime), "Not implemented");
282  }
283 
284  MemObjType getType() const override { return MemObjType::Undefined; }
285 
286  ContextImplPtr getInteropContext() const override { return MInteropContext; }
287 
288  bool isInterop() const override;
289 
290  bool hasUserDataPtr() const override { return MUserPtr != nullptr; }
291 
292  bool isHostPointerReadOnly() const override { return MHostPtrReadOnly; }
293 
294  bool usesPinnedHostMemory() const override {
295  return has_property<
297  }
298 
299  void detachMemoryObject(const std::shared_ptr<SYCLMemObjT> &Self) const;
300 
301  void markAsInternal() { MIsInternal = true; }
302 
305 
309 
313  // Compare exchange loop to safely decrement MGraphUseCount
314  while (true) {
315  size_t CurrentVal = MGraphUseCount;
316  if (CurrentVal == 0) {
317  break;
318  }
319  if (MGraphUseCount.compare_exchange_strong(CurrentVal, CurrentVal - 1) ==
320  false) {
321  continue;
322  }
323  }
324  }
325 
327  bool isUsedInGraph() const { return MGraphUseCount > 0; }
328 
329  const property_list &getPropList() const { return MProps; }
330 
331 protected:
332  // An allocateMem helper that determines which host ptr to use
333  void determineHostPtr(const ContextImplPtr &Context, bool InitFromUserData,
334  void *&HostPtr, bool &HostPtrReadOnly);
335 
336  // Allocator used for allocation memory on host.
337  std::unique_ptr<SYCLMemObjAllocator> MAllocator;
338  // Properties passed by user.
340  // Event passed by user to interoperability constructor.
341  // Should wait on this event before start working with such memory object.
343  // Context passed by user to interoperability constructor.
345  // Native backend memory object handle passed by user to interoperability
346  // constructor.
347  ur_mem_handle_t MInteropMemObject;
348  // Indicates whether memory object is created using interoperability
349  // constructor or not.
351  // Indicates if user provided pointer is read only.
353  // Indicates if memory object should write memory to the host on destruction.
355  // Size of memory.
356  size_t MSizeInBytes = 0;
357  // User's pointer passed to constructor.
358  void *MUserPtr;
359  // Copy of memory passed by user to constructor.
360  void *MShadowCopy;
361  // Function which update host with final data on memory object destruction.
362  std::function<void(void)> MUploadDataFunctor;
363  // Field which holds user's shared_ptr in case of memory object is created
364  // using constructor with shared_ptr.
365  std::shared_ptr<const void> MSharedPtrStorage;
366  // Field to identify if dtor is not necessarily blocking.
367  // check for MUploadDataFunctor is not enough to define it since for case when
368  // we have read only HostPtr - MUploadDataFunctor is empty but delayed release
369  // must be not allowed.
371  // Indicates that the memory object was allocated internally. Such memory
372  // objects can be released in a deferred manner regardless of whether a host
373  // pointer was provided or not.
374  bool MIsInternal = false;
375  // The number of graphs which are currently using this memory object.
376  std::atomic<size_t> MGraphUseCount = 0;
377  // Function which creates a shadow copy of the host pointer. This is used to
378  // defer the memory allocation and copying to the point where a writable
379  // accessor is created.
380  std::function<void(void)> MCreateShadowCopy = []() -> void {};
382  bool MOwnNativeHandle = true;
383 };
384 } // namespace detail
385 } // namespace _V1
386 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
const std::unique_ptr< SYCLMemObjAllocator > & get_allocator_internal() const
ContextImplPtr getInteropContext() const override
const PluginPtr & getPlugin() const
__SYCL2020_DEPRECATED("get_count() is deprecated, please use size() instead") size_t get_count() const
void detachMemoryObject(const std::shared_ptr< SYCLMemObjT > &Self) const
virtual ~SYCLMemObjT()=default
bool has_property() const noexcept
void markNoLongerBeingUsedInGraph()
Decrement an internal counter for how many graphs are currently using this memory object.
size_t getSizeInBytes() const noexcept override
void determineHostPtr(const ContextImplPtr &Context, bool InitFromUserData, void *&HostPtr, bool &HostPtrReadOnly)
void handleHostData(const std::shared_ptr< void > &HostPtr, const size_t RequiredAlign, bool IsConstPtr)
void releaseHostMem(void *Ptr) override
SYCLMemObjT(const property_list &Props, std::unique_ptr< SYCLMemObjAllocator > Allocator)
bool needsWriteBack() const
Returns true if this memory object requires a write_back on destruction.
std::shared_ptr< const void > MSharedPtrStorage
const property_list & getPropList() const
MemObjType getType() const override
std::atomic< size_t > MGraphUseCount
bool hasUserDataPtr() const override
void releaseMem(ContextImplPtr Context, void *MemAllocation) override
void addOrReplaceAccessorProperties(const property_list &PropertyList)
size_t size() const noexcept
void setAlign(size_t RequiredAlign)
SYCLMemObjT(cl_mem MemObject, const context &SyclContext, event AvailableEvent, std::unique_ptr< SYCLMemObjAllocator > Allocator)
void markBeingUsedInGraph()
Increment an internal counter for how many graphs are currently using this memory object.
bool canReadHostPtr(void *HostPtr, const size_t RequiredAlign)
SYCLMemObjT(const size_t SizeInBytes, const property_list &Props, std::unique_ptr< SYCLMemObjAllocator > Allocator)
std::function< void(void)> MUploadDataFunctor
void set_final_data(const std::function< void(const std::function< void(void *const Ptr)> &)> &FinalDataFunc)
bool isHostPointerReadOnly() const override
void set_final_data(std::nullptr_t)
void handleHostData(const void *HostPtr, const size_t RequiredAlign)
void deleteAccessorProperty(const PropWithDataKind &Kind)
void set_write_back(bool NeedWriteBack)
std::unique_ptr< SYCLMemObjAllocator > MAllocator
std::function< void(void)> MCreateShadowCopy
bool isInterop() const override
static size_t getBufSizeForContext(const ContextImplPtr &Context, ur_native_handle_t MemObject)
void * allocateMem(ContextImplPtr Context, bool InitFromUserData, void *HostPtr, ur_event_handle_t &InteropEvent) override
bool usesPinnedHostMemory() const override
bool isUsedInGraph() const
Returns true if any graphs are currently using this memory object.
void handleHostData(void *HostPtr, const size_t RequiredAlign)
void handleHostData(const std::function< void(void *)> &CopyFromInput, const size_t RequiredAlign, bool IsConstPtr)
bool canReuseHostPtr(void *HostPtr, const size_t RequiredAlign)
An event object can be used to synchronize memory transfers, enqueues of kernels and signaling barrie...
Definition: event.hpp:44
Objects of the property_list class are containers for the SYCL properties.
bool has_property() const noexcept
void delete_accessor_property(const sycl::detail::PropWithDataKind &Kind)
void add_or_replace_accessor_properties(const property_list &PropertyList)
To cast(std::vector< cl_event > value)
std::shared_ptr< sycl::detail::context_impl > ContextImplPtr
Definition: event_impl.hpp:32
std::shared_ptr< event_impl > EventImplPtr
Definition: handler.hpp:183
std::shared_ptr< plugin > PluginPtr
Definition: ur.hpp:107
class __SYCL_EBO __SYCL_SPECIAL_CLASS Dimensions
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:65
Definition: access.hpp:18
_Abi const simd< _Tp, _Abi > & noexcept
Definition: simd.hpp:1324
C++ utilities for Unified Runtime integration.