DPC++ Runtime
Runtime libraries for oneAPI DPC++
device_impl.cpp
Go to the documentation of this file.
1 //==----------------- device_impl.cpp - SYCL device ------------------------==//
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/device_impl.hpp>
10 #include <detail/platform_impl.hpp>
11 #include <sycl/device.hpp>
12 
13 #include <algorithm>
14 
15 namespace sycl {
17 namespace detail {
18 
19 device_impl::device_impl()
20  : MIsHostDevice(true), MPlatform(platform_impl::getHostPlatformImpl()),
21  // assert is natively supported by host
22  MIsAssertFailSupported(true) {}
23 
25  const plugin &Plugin)
26  : device_impl(InteropDeviceHandle, nullptr, nullptr, Plugin) {}
27 
29  : device_impl(reinterpret_cast<pi_native_handle>(nullptr), Device, Platform,
30  Platform->getPlugin()) {}
31 
33  : device_impl(reinterpret_cast<pi_native_handle>(nullptr), Device, nullptr,
34  Plugin) {}
35 
36 device_impl::device_impl(pi_native_handle InteropDeviceHandle,
38  const plugin &Plugin)
39  : MDevice(Device), MIsHostDevice(false) {
40 
41  bool InteroperabilityConstructor = false;
42  if (Device == nullptr) {
43  assert(InteropDeviceHandle);
44  // Get PI device from the raw device handle.
45  // NOTE: this is for OpenCL interop only (and should go away).
46  // With SYCL-2020 BE generalization "make" functions are used instead.
48  InteropDeviceHandle, nullptr, &MDevice);
49  InteroperabilityConstructor = true;
50  }
51 
52  // TODO catch an exception and put it to list of asynchronous exceptions
54  MDevice, PI_DEVICE_INFO_TYPE, sizeof(RT::PiDeviceType), &MType, nullptr);
55 
56  // No need to set MRootDevice when MAlwaysRootDevice is true
57  if ((Platform == nullptr) || !Platform->MAlwaysRootDevice) {
58  // TODO catch an exception and put it to list of asynchronous exceptions
61  &MRootDevice, nullptr);
62  }
63 
64  if (!InteroperabilityConstructor) {
65  // TODO catch an exception and put it to list of asynchronous exceptions
66  // Interoperability Constructor already calls DeviceRetain in
67  // piextDeviceFromNative.
68  Plugin.call<PiApiKind::piDeviceRetain>(MDevice);
69  }
70 
71  // set MPlatform
72  if (!Platform) {
73  Platform = platform_impl::getPlatformFromPiDevice(MDevice, Plugin);
74  }
75  MPlatform = Platform;
76 
77  MIsAssertFailSupported =
79 }
80 
82  if (!MIsHostDevice) {
83  // TODO catch an exception and put it to list of asynchronous exceptions
84  const detail::plugin &Plugin = getPlugin();
87  }
88 }
89 
91  info::partition_affinity_domain AffinityDomain) const {
92  auto SupportedDomains = get_info<info::device::partition_affinity_domains>();
93  return std::find(SupportedDomains.begin(), SupportedDomains.end(),
94  AffinityDomain) != SupportedDomains.end();
95 }
96 
97 cl_device_id device_impl::get() const {
98  if (MIsHostDevice) {
99  throw invalid_object_error(
100  "This instance of device doesn't support OpenCL interoperability.",
101  PI_ERROR_INVALID_DEVICE);
102  }
103  // TODO catch an exception and put it to list of asynchronous exceptions
105  return pi::cast<cl_device_id>(getNative());
106 }
107 
109  return createSyclObjFromImpl<platform>(MPlatform);
110 }
111 
112 bool device_impl::has_extension(const std::string &ExtensionName) const {
113  if (MIsHostDevice)
114  // TODO: implement extension management for host device;
115  return false;
116 
117  std::string AllExtensionNames = get_device_info_string(
119  this->getPlugin());
120  return (AllExtensionNames.find(ExtensionName) != std::string::npos);
121 }
122 
124  auto SupportedProperties = get_info<info::device::partition_properties>();
125  return std::find(SupportedProperties.begin(), SupportedProperties.end(),
126  Prop) != SupportedProperties.end();
127 }
128 
129 std::vector<device>
130 device_impl::create_sub_devices(const cl_device_partition_property *Properties,
131  size_t SubDevicesCount) const {
132 
133  std::vector<RT::PiDevice> SubDevices(SubDevicesCount);
134  pi_uint32 ReturnedSubDevices = 0;
135  const detail::plugin &Plugin = getPlugin();
136  Plugin.call<sycl::errc::invalid, PiApiKind::piDevicePartition>(
137  MDevice, Properties, SubDevicesCount, SubDevices.data(),
138  &ReturnedSubDevices);
139  if (ReturnedSubDevices != SubDevicesCount) {
140  throw sycl::exception(
142  "Could not partition to the specified number of sub-devices");
143  }
144  // TODO: Need to describe the subdevice model. Some sub_device management
145  // may be necessary. What happens if create_sub_devices is called multiple
146  // times with the same arguments?
147  //
148  std::vector<device> res;
149  std::for_each(SubDevices.begin(), SubDevices.end(),
150  [&res, this](const RT::PiDevice &a_pi_device) {
151  device sycl_device = detail::createSyclObjFromImpl<device>(
152  MPlatform->getOrMakeDeviceImpl(a_pi_device, MPlatform));
153  res.push_back(sycl_device);
154  });
155  return res;
156 }
157 
158 std::vector<device> device_impl::create_sub_devices(size_t ComputeUnits) const {
159  assert(!MIsHostDevice && "Partitioning is not supported on host.");
160 
162  throw sycl::feature_not_supported(
163  "Device does not support "
164  "sycl::info::partition_property::partition_equally.",
165  PI_ERROR_INVALID_OPERATION);
166  }
167  // If count exceeds the total number of compute units in the device, an
168  // exception with the errc::invalid error code must be thrown.
169  auto MaxComputeUnits = get_info<info::device::max_compute_units>();
170  if (ComputeUnits > MaxComputeUnits)
171  throw sycl::exception(errc::invalid,
172  "Total counts exceed max compute units");
173 
174  size_t SubDevicesCount = MaxComputeUnits / ComputeUnits;
175  const pi_device_partition_property Properties[3] = {
177  0};
178  return create_sub_devices(Properties, SubDevicesCount);
179 }
180 
181 std::vector<device>
182 device_impl::create_sub_devices(const std::vector<size_t> &Counts) const {
183  assert(!MIsHostDevice && "Partitioning is not supported on host.");
184 
186  throw sycl::feature_not_supported(
187  "Device does not support "
188  "sycl::info::partition_property::partition_by_counts.",
189  PI_ERROR_INVALID_OPERATION);
190  }
191  static const pi_device_partition_property P[] = {
193  std::vector<pi_device_partition_property> Properties(P, P + 3);
194 
195  // Fill the properties vector with counts and validate it
196  auto It = Properties.begin() + 1;
197  size_t TotalCounts = 0;
198  size_t NonZeroCounts = 0;
199  for (auto Count : Counts) {
200  TotalCounts += Count;
201  NonZeroCounts += (Count != 0) ? 1 : 0;
202  It = Properties.insert(It, Count);
203  }
204 
205  // If the number of non-zero values in counts exceeds the device’s maximum
206  // number of sub devices (as returned by info::device::
207  // partition_max_sub_devices) an exception with the errc::invalid
208  // error code must be thrown.
209  if (NonZeroCounts > get_info<info::device::partition_max_sub_devices>())
210  throw sycl::exception(errc::invalid,
211  "Total non-zero counts exceed max sub-devices");
212 
213  // If the total of all the values in the counts vector exceeds the total
214  // number of compute units in the device (as returned by
215  // info::device::max_compute_units), an exception with the errc::invalid
216  // error code must be thrown.
217  if (TotalCounts > get_info<info::device::max_compute_units>())
218  throw sycl::exception(errc::invalid,
219  "Total counts exceed max compute units");
220 
221  return create_sub_devices(Properties.data(), Counts.size());
222 }
223 
225  info::partition_affinity_domain AffinityDomain) const {
226  assert(!MIsHostDevice && "Partitioning is not supported on host.");
227 
230  throw sycl::feature_not_supported(
231  "Device does not support "
232  "sycl::info::partition_property::partition_by_affinity_domain.",
233  PI_ERROR_INVALID_OPERATION);
234  }
235  if (!is_affinity_supported(AffinityDomain)) {
236  throw sycl::feature_not_supported(
237  "Device does not support " + affinityDomainToString(AffinityDomain) +
238  ".",
239  PI_ERROR_INVALID_VALUE);
240  }
241  const pi_device_partition_property Properties[3] = {
243  (pi_device_partition_property)AffinityDomain, 0};
244 
245  pi_uint32 SubDevicesCount = 0;
246  const detail::plugin &Plugin = getPlugin();
247  Plugin.call<sycl::errc::invalid, PiApiKind::piDevicePartition>(
248  MDevice, Properties, 0, nullptr, &SubDevicesCount);
249 
250  return create_sub_devices(Properties, SubDevicesCount);
251 }
252 
254  auto Plugin = getPlugin();
255  if (Plugin.getBackend() == backend::opencl)
257  pi_native_handle Handle;
259  return Handle;
260 }
261 
262 bool device_impl::has(aspect Aspect) const {
263  size_t return_size = 0;
264 
265  switch (Aspect) {
266  case aspect::host:
267  return is_host();
268  case aspect::cpu:
269  return is_cpu();
270  case aspect::gpu:
271  return is_gpu();
272  case aspect::accelerator:
273  return is_accelerator();
274  case aspect::custom:
275  return false;
276  case aspect::fp16:
277  return has_extension("cl_khr_fp16");
278  case aspect::fp64:
279  return has_extension("cl_khr_fp64");
280  case aspect::ext_oneapi_bfloat16_math_functions:
281  return get_info<info::device::ext_oneapi_bfloat16_math_functions>();
282  case aspect::int64_base_atomics:
283  return has_extension("cl_khr_int64_base_atomics");
284  case aspect::int64_extended_atomics:
285  return has_extension("cl_khr_int64_extended_atomics");
286  case aspect::atomic64:
287  return get_info<info::device::atomic64>();
288  case aspect::image:
289  return get_info<info::device::image_support>();
290  case aspect::online_compiler:
291  return get_info<info::device::is_compiler_available>();
292  case aspect::online_linker:
293  return get_info<info::device::is_linker_available>();
294  case aspect::queue_profiling:
295  return get_info<info::device::queue_profiling>();
296  case aspect::usm_device_allocations:
297  return get_info<info::device::usm_device_allocations>();
298  case aspect::usm_host_allocations:
299  return get_info<info::device::usm_host_allocations>();
300  case aspect::usm_atomic_host_allocations:
301  return is_host() ||
304  info::device::usm_host_allocations>::get(MDevice, getPlugin()) &
306  case aspect::usm_shared_allocations:
307  return get_info<info::device::usm_shared_allocations>();
308  case aspect::usm_atomic_shared_allocations:
309  return is_host() ||
312  info::device::usm_shared_allocations>::get(MDevice,
313  getPlugin()) &
315  case aspect::usm_restricted_shared_allocations:
316  return get_info<info::device::usm_restricted_shared_allocations>();
317  case aspect::usm_system_allocations:
318  return get_info<info::device::usm_system_allocations>();
319  case aspect::ext_intel_device_id:
321  MDevice, PI_DEVICE_INFO_DEVICE_ID, 0, nullptr, &return_size) ==
322  PI_SUCCESS;
323  case aspect::ext_intel_pci_address:
325  MDevice, PI_DEVICE_INFO_PCI_ADDRESS, 0, nullptr, &return_size) ==
326  PI_SUCCESS;
327  case aspect::ext_intel_gpu_eu_count:
329  MDevice, PI_DEVICE_INFO_GPU_EU_COUNT, 0, nullptr,
330  &return_size) == PI_SUCCESS;
331  case aspect::ext_intel_gpu_eu_simd_width:
333  MDevice, PI_DEVICE_INFO_GPU_EU_SIMD_WIDTH, 0, nullptr,
334  &return_size) == PI_SUCCESS;
335  case aspect::ext_intel_gpu_slices:
337  MDevice, PI_DEVICE_INFO_GPU_SLICES, 0, nullptr, &return_size) ==
338  PI_SUCCESS;
339  case aspect::ext_intel_gpu_subslices_per_slice:
341  MDevice, PI_DEVICE_INFO_GPU_SUBSLICES_PER_SLICE, 0, nullptr,
342  &return_size) == PI_SUCCESS;
343  case aspect::ext_intel_gpu_eu_count_per_subslice:
345  MDevice, PI_DEVICE_INFO_GPU_EU_COUNT_PER_SUBSLICE, 0, nullptr,
346  &return_size) == PI_SUCCESS;
347  case aspect::ext_intel_gpu_hw_threads_per_eu:
349  MDevice, PI_DEVICE_INFO_GPU_HW_THREADS_PER_EU, 0, nullptr,
350  &return_size) == PI_SUCCESS;
351  case aspect::ext_intel_free_memory:
353  MDevice, PI_EXT_INTEL_DEVICE_INFO_FREE_MEMORY, 0, nullptr,
354  &return_size) == PI_SUCCESS;
355  case aspect::ext_intel_memory_clock_rate:
357  MDevice, PI_EXT_INTEL_DEVICE_INFO_MEMORY_CLOCK_RATE, 0, nullptr,
358  &return_size) == PI_SUCCESS;
359  case aspect::ext_intel_memory_bus_width:
361  MDevice, PI_EXT_INTEL_DEVICE_INFO_MEMORY_BUS_WIDTH, 0, nullptr,
362  &return_size) == PI_SUCCESS;
363  case aspect::ext_intel_device_info_uuid: {
365  MDevice, PI_DEVICE_INFO_UUID, 0, nullptr, &return_size);
366  if (Result != PI_SUCCESS) {
367  return false;
368  }
369 
370  assert(return_size <= 16);
371  unsigned char UUID[16];
372 
374  MDevice, PI_DEVICE_INFO_UUID, 16 * sizeof(unsigned char), UUID,
375  nullptr) == PI_SUCCESS;
376  }
377  case aspect::ext_intel_max_mem_bandwidth:
378  // currently not supported
379  return false;
380  case aspect::ext_oneapi_srgb:
381  return get_info<info::device::ext_oneapi_srgb>();
382  case aspect::ext_oneapi_native_assert:
383  return isAssertFailSupported();
384  case aspect::ext_oneapi_cuda_async_barrier: {
385  int async_barrier_supported;
386  bool call_successful =
388  MDevice, PI_EXT_ONEAPI_DEVICE_INFO_CUDA_ASYNC_BARRIER, sizeof(int),
389  &async_barrier_supported, nullptr) == PI_SUCCESS;
390  return call_successful && async_barrier_supported;
391  }
392  default:
393  throw runtime_error("This device aspect has not been implemented yet.",
394  PI_ERROR_INVALID_DEVICE);
395  }
396 }
397 
398 std::shared_ptr<device_impl> device_impl::getHostDeviceImpl() {
399  static std::shared_ptr<device_impl> HostImpl =
400  std::make_shared<device_impl>();
401 
402  return HostImpl;
403 }
404 
406  return MIsAssertFailSupported;
407 }
408 
409 std::string device_impl::getDeviceName() const {
410  std::call_once(MDeviceNameFlag,
411  [this]() { MDeviceName = get_info<info::device::name>(); });
412 
413  return MDeviceName;
414 }
415 
416 } // namespace detail
417 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
418 } // namespace sycl
std::vector< device > create_sub_devices(const cl_device_partition_property *Properties, size_t SubDevicesCount) const
bool has(aspect Aspect) const
Indicates if the SYCL device has the given feature.
bool is_host() const
Check if SYCL device is a host device.
Definition: device_impl.hpp:88
platform get_platform() const
Get associated SYCL platform.
device_impl()
Constructs a SYCL device instance as a host device.
Definition: device_impl.cpp:19
bool is_cpu() const
Check if device is a CPU device.
Definition: device_impl.hpp:93
pi_native_handle getNative() const
Gets the native handle of the SYCL device.
bool is_gpu() const
Check if device is a GPU device.
Definition: device_impl.hpp:98
std::string getDeviceName() const
bool has_extension(const std::string &ExtensionName) const
Check SYCL extension support by device.
const plugin & getPlugin() const
RT::PiDevice & getHandleRef()
Get reference to PI device.
Definition: device_impl.hpp:64
bool is_affinity_supported(info::partition_affinity_domain AffinityDomain) const
Check if affinity partitioning by specified domain is supported by device.
Definition: device_impl.cpp:90
cl_device_id get() const
Get instance of OpenCL device.
Definition: device_impl.cpp:97
bool is_partition_supported(info::partition_property Prop) const
Check if desired partition property supported by device.
static std::shared_ptr< device_impl > getHostDeviceImpl()
Gets the single instance of the Host Device.
bool is_accelerator() const
Check if device is an accelerator device.
static std::shared_ptr< platform_impl > getPlatformFromPiDevice(RT::PiDevice PiDevice, const plugin &Plugin)
Queries the cache for the specified platform based on an input device.
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
backend getBackend(void) const
Definition: plugin.hpp:229
RT::PiResult call_nocheck(ArgsT... Args) const
Calls the PiApi, traces the call, and returns the result.
Definition: plugin.hpp:170
Encapsulates a SYCL platform on which kernels may be executed.
Definition: platform.hpp:45
#define __SYCL_CHECK_OCL_CODE_NO_EXC(X)
Definition: common.hpp:218
#define __SYCL_INLINE_VER_NAMESPACE(X)
::pi_device PiDevice
Definition: pi.hpp:110
::pi_device_type PiDeviceType
Definition: pi.hpp:111
::pi_result PiResult
Definition: pi.hpp:108
static const plugin & getPlugin(backend Backend)
Definition: backend.cpp:32
std::string affinityDomainToString(info::partition_affinity_domain AffinityDomain)
Definition: device_info.hpp:81
std::string get_device_info_string(RT::PiDevice dev, RT::PiDeviceInfo InfoCode, const plugin &Plugin)
std::shared_ptr< detail::platform_impl > PlatformImplPtr
Function for_each(Group g, Ptr first, Ptr last, Function f)
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
#define PI_DEVICE_INFO_EXTENSION_DEVICELIB_ASSERT
Extension to denote native support of assert feature by an arbitrary device piDeviceGetInfo call shou...
Definition: pi.h:740
uintptr_t pi_native_handle
Definition: pi.h:107
pi_result piextDeviceGetNativeHandle(pi_device device, pi_native_handle *nativeHandle)
Gets the native handle of a PI device object.
_pi_usm_capabilities pi_usm_capabilities
Definition: pi.h:1644
@ PI_DEVICE_INFO_GPU_SLICES
Definition: pi.h:275
@ PI_DEVICE_INFO_GPU_EU_COUNT
Definition: pi.h:273
@ PI_EXT_INTEL_DEVICE_INFO_FREE_MEMORY
Definition: pi.h:282
@ PI_DEVICE_INFO_UUID
Definition: pi.h:269
@ PI_DEVICE_INFO_PARENT_DEVICE
Definition: pi.h:255
@ PI_DEVICE_INFO_GPU_EU_SIMD_WIDTH
Definition: pi.h:274
@ PI_DEVICE_INFO_GPU_HW_THREADS_PER_EU
Definition: pi.h:292
@ PI_DEVICE_INFO_DEVICE_ID
Definition: pi.h:271
@ PI_DEVICE_INFO_PCI_ADDRESS
Definition: pi.h:272
@ PI_EXT_ONEAPI_DEVICE_INFO_CUDA_ASYNC_BARRIER
Definition: pi.h:300
@ PI_DEVICE_INFO_GPU_EU_COUNT_PER_SUBSLICE
Definition: pi.h:277
@ PI_EXT_INTEL_DEVICE_INFO_MEMORY_CLOCK_RATE
Definition: pi.h:285
@ PI_EXT_INTEL_DEVICE_INFO_MEMORY_BUS_WIDTH
Definition: pi.h:288
@ PI_DEVICE_INFO_TYPE
Definition: pi.h:184
@ PI_DEVICE_INFO_GPU_SUBSLICES_PER_SLICE
Definition: pi.h:276
static constexpr pi_device_partition_property PI_DEVICE_PARTITION_BY_COUNTS
Definition: pi.h:618
pi_result piDeviceGetInfo(pi_device device, pi_device_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
Returns requested info for provided native device Return PI_DEVICE_INFO_EXTENSION_DEVICELIB_ASSERT fo...
@ PI_USM_CONCURRENT_ATOMIC_ACCESS
Definition: pi.h:1619
static constexpr pi_device_partition_property PI_DEVICE_PARTITION_BY_COUNTS_LIST_END
Definition: pi.h:621
static constexpr pi_device_partition_property PI_DEVICE_PARTITION_EQUALLY
Definition: pi.h:616
uint32_t pi_uint32
Definition: pi.h:103
static constexpr pi_device_partition_property PI_DEVICE_PARTITION_BY_AFFINITY_DOMAIN
Definition: pi.h:623
pi_result piextDeviceCreateWithNativeHandle(pi_native_handle nativeHandle, pi_platform platform, pi_device *device)
Creates PI device object from a native handle.
pi_result piDeviceRetain(pi_device device)
pi_result piDeviceRelease(pi_device device)
intptr_t pi_device_partition_property
Definition: pi.h:615
pi_result piDevicePartition(pi_device device, const pi_device_partition_property *properties, pi_uint32 num_devices, pi_device *out_devices, pi_uint32 *out_num_devices)
@ Device