DPC++ Runtime
Runtime libraries for oneAPI DPC++
device_selector.cpp
Go to the documentation of this file.
1 //==------ device_selector.cpp - SYCL device selector ----------------------==//
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/config.hpp>
10 #include <detail/device_impl.hpp>
14 #include <sycl/backend_types.hpp>
16 #include <sycl/device.hpp>
17 #include <sycl/device_selector.hpp>
18 #include <sycl/exception.hpp>
20 // 4.6.1 Device selection class
21 
22 #include <algorithm>
23 #include <cctype>
24 #include <regex>
25 
26 namespace sycl {
27 inline namespace _V1 {
28 
29 namespace detail {
30 
31 // ONEAPI_DEVICE_SELECTOR doesn't need to be considered in the device
32 // preferences as it filters the device list returned by device::get_devices
33 // itself, so only matching devices will be scored.
34 static int getDevicePreference(const device &Device) {
35  int Score = 0;
36 
37  // Strongly prefer devices with available images.
38  auto &program_manager = sycl::detail::ProgramManager::getInstance();
39  if (program_manager.hasCompatibleImage(Device))
40  Score += 1000;
41 
42  // Prefer level_zero backend devices.
43  if (detail::getSyclObjImpl(Device)->getBackend() ==
45  Score += 50;
46 
47  return Score;
48 }
49 
50 static void traceDeviceSelection(const device &Device, int Score, bool Chosen) {
51  bool shouldTrace = false;
52  if (Chosen) {
54  } else {
56  }
57  if (shouldTrace) {
58  std::string PlatformName = Device.get_info<info::device::platform>()
59  .get_info<info::platform::name>();
60  std::string DeviceName = Device.get_info<info::device::name>();
61  auto selectionMsg = Chosen ? "Selected device: -> final score = "
62  : "Candidate device: -> score = ";
63 
64  std::cout << "SYCL_PI_TRACE[all]: " << selectionMsg << Score
65  << ((Score < 0) ? " (REJECTED)" : "") << std::endl
66  << "SYCL_PI_TRACE[all]: "
67  << " platform: " << PlatformName << std::endl
68  << "SYCL_PI_TRACE[all]: "
69  << " device: " << DeviceName << std::endl;
70  }
71 }
72 
74  std::vector<device> &Devices) {
75  int score = detail::REJECT_DEVICE_SCORE;
76  const device *res = nullptr;
77 
78  for (const auto &dev : Devices) {
79  int dev_score = DeviceSelectorInvocable(dev);
80 
81  traceDeviceSelection(dev, dev_score, false);
82 
83  // A negative score means that a device must not be selected.
84  if (dev_score < 0)
85  continue;
86 
87  // Section 4.6 of SYCL 1.2.1 spec:
88  // "If more than one device receives the high score then
89  // one of those tied devices will be returned, but which of the devices
90  // from the tied set is to be returned is not defined". So use the device
91  // preference score to resolve ties, this is necessary for custom_selectors
92  // that may not already include device preference in their scoring.
93 
94  if ((score < dev_score) ||
95  ((score == dev_score) &&
96  (getDevicePreference(*res) < getDevicePreference(dev)))) {
97  res = &dev;
98  score = dev_score;
99  }
100  }
101 
102  if (res != nullptr) {
103  traceDeviceSelection(*res, score, true);
104 
105  return *res;
106  }
107 
108  std::string Message;
109  constexpr const char Prefix[] = "No device of requested type ";
110  constexpr const char Cpu[] = "'info::device_type::cpu' ";
111  constexpr const char Gpu[] = "'info::device_type::gpu' ";
112  constexpr const char Acc[] = "'info::device_type::accelerator' ";
113  constexpr const char Suffix[] = "available.";
114  constexpr auto ReserveSize = sizeof(Prefix) + sizeof(Suffix) + sizeof(Acc);
115  Message.reserve(ReserveSize);
116  Message += Prefix;
117 
118  auto Selector =
119  DeviceSelectorInvocable.target<int (*)(const sycl::device &)>();
120  if ((Selector && *Selector == gpu_selector_v) ||
121  DeviceSelectorInvocable.target<sycl::gpu_selector>()) {
122  Message += Gpu;
123  } else if ((Selector && *Selector == cpu_selector_v) ||
124  DeviceSelectorInvocable.target<sycl::cpu_selector>()) {
125  Message += Cpu;
126  } else if ((Selector && *Selector == accelerator_selector_v) ||
127  DeviceSelectorInvocable.target<sycl::accelerator_selector>()) {
128  Message += Acc;
129  }
130  Message += Suffix;
131  throw sycl::runtime_error(Message, PI_ERROR_DEVICE_NOT_FOUND);
132 }
133 
134 // select_device(selector)
135 __SYCL_EXPORT device
136 select_device(const DSelectorInvocableType &DeviceSelectorInvocable) {
137  std::vector<device> Devices = device::get_devices();
138 
139  return select_device(DeviceSelectorInvocable, Devices);
140 }
141 
142 // select_device(selector, context)
143 __SYCL_EXPORT device
144 select_device(const DSelectorInvocableType &DeviceSelectorInvocable,
145  const context &SyclContext) {
146  device SelectedDevice = select_device(DeviceSelectorInvocable);
147 
148  // Throw exception if selected device is not in context.
149  std::vector<device> Devices = SyclContext.get_devices();
150  if (std::find(Devices.begin(), Devices.end(), SelectedDevice) ==
151  Devices.end())
153  "Selected device is not in the given context.");
154 
155  return SelectedDevice;
156 }
157 
158 } // namespace detail
159 
160 // -------------- SYCL 2020
161 
168 
169 static void traceDeviceSelector(const std::string &DeviceType) {
170  bool ShouldTrace = false;
172  if (ShouldTrace) {
173  std::cout << "SYCL_PI_TRACE[all]: Requested device_type: " << DeviceType
174  << std::endl;
175  }
176 }
177 
178 __SYCL_EXPORT int default_selector_v(const device &dev) {
179  // The default selector doesn't reject any devices.
180  int Score = 0;
181 
182  // we give the esimd_emulator device a score of zero to prevent it from being
183  // chosen among other devices. The same thing is done for gpu_selector_v
184  // below.
185  if (dev.get_backend() == backend::ext_intel_esimd_emulator) {
186  return 0;
187  }
188 
189  traceDeviceSelector("info::device_type::automatic");
190 
191  if (dev.is_gpu())
192  Score += 500;
193 
194  if (dev.is_cpu())
195  Score += 300;
196 
197  // Since we deprecate SYCL_BE and SYCL_DEVICE_TYPE,
198  // we should not disallow accelerator to be chosen.
199  // But this device type gets the lowest heuristic point.
200  if (dev.is_accelerator())
201  Score += 75;
202 
203  // Add preference score.
204  Score += detail::getDevicePreference(dev);
205 
206  return Score;
207 }
208 
209 __SYCL_EXPORT int gpu_selector_v(const device &dev) {
210  int Score = detail::REJECT_DEVICE_SCORE;
211 
212  if (dev.get_backend() == backend::ext_intel_esimd_emulator) {
213  return 0;
214  }
215 
216  traceDeviceSelector("info::device_type::gpu");
217  if (dev.is_gpu()) {
218  Score = 1000;
219  Score += detail::getDevicePreference(dev);
220  }
221  return Score;
222 }
223 
224 __SYCL_EXPORT int cpu_selector_v(const device &dev) {
225  int Score = detail::REJECT_DEVICE_SCORE;
226 
227  traceDeviceSelector("info::device_type::cpu");
228  if (dev.is_cpu()) {
229  Score = 1000;
230  Score += detail::getDevicePreference(dev);
231  }
232  return Score;
233 }
234 
235 __SYCL_EXPORT int accelerator_selector_v(const device &dev) {
236  int Score = detail::REJECT_DEVICE_SCORE;
237 
238  traceDeviceSelector("info::device_type::accelerator");
239  if (dev.is_accelerator()) {
240  Score = 1000;
241  Score += detail::getDevicePreference(dev);
242  }
243  return Score;
244 }
245 
246 int host_selector::operator()(const device &dev) const {
247  // Host device has been removed and host_selector has been deprecated, so this
248  // should never be able to select a device.
249  std::ignore = dev;
250  traceDeviceSelector("info::device_type::host");
252 }
253 
254 __SYCL_EXPORT detail::DSelectorInvocableType
255 aspect_selector(const std::vector<aspect> &RequireList,
256  const std::vector<aspect> &DenyList /* ={} */) {
257  return [=](const sycl::device &Dev) {
258  auto DevHas = [&](const aspect &Asp) -> bool { return Dev.has(Asp); };
259 
260  // All aspects from require list are required.
261  if (!std::all_of(RequireList.begin(), RequireList.end(), DevHas))
263 
264  // No aspect from deny list is allowed
265  if (std::any_of(DenyList.begin(), DenyList.end(), DevHas))
267 
268  if (RequireList.size() > 0) {
269  return 1000 + detail::getDevicePreference(Dev);
270  } else {
271  // No required aspects specified.
272  // SYCL 2020 4.6.1.1 "If no aspects are passed in, the generated selector
273  // behaves like default_selector."
274  return default_selector_v(Dev);
275  }
276  };
277 }
278 
279 // -------------- SYCL 1.2.1
280 
281 // SYCL 1.2.1 device_selector class and sub-classes
282 
283 device device_selector::select_device() const {
284  return detail::select_device([&](const device &dev) { return (*this)(dev); });
285 }
286 
287 int default_selector::operator()(const device &dev) const {
288  return default_selector_v(dev);
289 }
290 
291 int gpu_selector::operator()(const device &dev) const {
292  return gpu_selector_v(dev);
293 }
294 
295 int cpu_selector::operator()(const device &dev) const {
296  return cpu_selector_v(dev);
297 }
298 
300  return accelerator_selector_v(dev);
301 }
302 
303 namespace ext::oneapi {
304 
305 filter_selector::filter_selector(const std::string &Input)
306  : impl(std::make_shared<detail::filter_selector_impl>(Input)) {}
307 
308 int filter_selector::operator()(const device &Dev) const {
309  return impl->operator()(Dev);
310 }
311 
312 void filter_selector::reset() const { impl->reset(); }
313 
314 // filter_selectors not "Callable"
315 // because of the requirement that the filter_selector "reset()" itself
316 // between invocations, the filter_selector operator() is not purely callable
317 // and cannot be used interchangeably as a SYCL2020 callable device selector.
318 // TODO: replace the FilterSelector subclass with something that
319 // doesn't pretend to be a device_selector, and instead is something that
320 // just returns a device (rather than a score).
321 // Then remove ! std::is_base_of_v<ext::oneapi::filter_selector, DeviceSelector>
322 // from device/platform/queue constructors
324  std::lock_guard<std::mutex> Guard(
325  sycl::detail::GlobalHandler::instance().getFilterMutex());
326 
328 
329  reset();
330 
331  return Result;
332 }
333 
334 } // namespace ext::oneapi
335 
336 namespace __SYCL2020_DEPRECATED("use 'ext::oneapi' instead") ONEAPI {
337 using namespace ext::oneapi;
338 filter_selector::filter_selector(const std::string &Input)
339  : ext::oneapi::filter_selector(Input) {}
340 
341 int filter_selector::operator()(const device &Dev) const {
343 }
344 
346 
347 device filter_selector::select_device() const {
349 }
350 } // namespace __SYCL2020_DEPRECATED("use 'ext::oneapi' instead")ONEAPI
351 } // namespace _V1
352 } // namespace sycl
The default selector chooses the first available SYCL device.
int operator()(const device &dev) const override
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
std::vector< device > get_devices() const
Gets devices associated with this SYCL context.
Definition: context.cpp:152
The SYCL device class encapsulates a single SYCL device on which kernels may be executed.
Definition: device.hpp:64
bool is_accelerator() const
Check if device is an accelerator device.
Definition: device.cpp:83
bool is_gpu() const
Check if device is a GPU device.
Definition: device.cpp:81
static std::vector< device > get_devices(info::device_type deviceType=info::device_type::all)
Query available SYCL devices.
Definition: device.cpp:51
backend get_backend() const noexcept
Returns the backend associated with this device.
Definition: device.cpp:215
detail::is_device_info_desc< Param >::return_type get_info() const
Queries this SYCL device for information requested by the template parameter param.
Definition: device.hpp:223
bool is_cpu() const
Check if device is a CPU device.
Definition: device.cpp:79
filter_selector(const std::string &filter)
int operator()(const device &dev) const override
class __SYCL2020_DEPRECATED("Host device is no longer supported.") host_selector int default_selector_v(const device &dev)
Selects SYCL host device.
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
bool trace(TraceLevel level)
Definition: pi.cpp:366
static int getDevicePreference(const device &Device)
decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:30
std::function< int(const sycl::device &)> DSelectorInvocableType
static constexpr int REJECT_DEVICE_SCORE
device select_device(const DSelectorInvocableType &DeviceSelectorInvocable)
static void traceDeviceSelection(const device &Device, int Score, bool Chosen)
int accelerator_selector_v(const device &dev)
signed char __SYCL2020_DEPRECATED
Definition: aliases.hpp:94
int cpu_selector_v(const device &dev)
std::uint8_t instead
Definition: aliases.hpp:93
detail::DSelectorInvocableType aspect_selector(const std::vector< aspect > &RequireList, const std::vector< aspect > &DenyList={})
int gpu_selector_v(const device &dev)
static void traceDeviceSelector(const std::string &DeviceType)
default_selector_v Devices of different kinds are prioritized in the following order:
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:93
Definition: access.hpp:18
bool any_of(const simd_mask< _Tp, _Abi > &) noexcept
bool all_of(const simd_mask< _Tp, _Abi > &) noexcept