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 = detail::ur::trace();
52  if (shouldTrace) {
53  std::string PlatformName = Device.get_info<info::device::platform>()
54  .get_info<info::platform::name>();
55  std::string DeviceName = Device.get_info<info::device::name>();
56  auto selectionMsg = Chosen ? "Selected device: -> final score = "
57  : "Candidate device: -> score = ";
58 
59  std::cout << "SYCL_UR_TRACE: " << selectionMsg << Score
60  << ((Score < 0) ? " (REJECTED)" : "") << std::endl
61  << "SYCL_UR_TRACE: "
62  << " platform: " << PlatformName << std::endl
63  << "SYCL_UR_TRACE: "
64  << " device: " << DeviceName << std::endl;
65  }
66 }
67 
69  std::vector<device> &Devices) {
70  int score = detail::REJECT_DEVICE_SCORE;
71  const device *res = nullptr;
72 
73  for (const auto &dev : Devices) {
74  int dev_score = DeviceSelectorInvocable(dev);
75 
76  traceDeviceSelection(dev, dev_score, false);
77 
78  // A negative score means that a device must not be selected.
79  if (dev_score < 0)
80  continue;
81 
82  // Section 4.6 of SYCL 1.2.1 spec:
83  // "If more than one device receives the high score then
84  // one of those tied devices will be returned, but which of the devices
85  // from the tied set is to be returned is not defined". So use the device
86  // preference score to resolve ties, this is necessary for custom_selectors
87  // that may not already include device preference in their scoring.
88 
89  if ((score < dev_score) ||
90  ((score == dev_score) &&
91  (getDevicePreference(*res) < getDevicePreference(dev)))) {
92  res = &dev;
93  score = dev_score;
94  }
95  }
96 
97  if (res != nullptr) {
98  traceDeviceSelection(*res, score, true);
99 
100  return *res;
101  }
102 
103  std::string Message;
104  constexpr const char Prefix[] = "No device of requested type ";
105  constexpr const char Cpu[] = "'info::device_type::cpu' ";
106  constexpr const char Gpu[] = "'info::device_type::gpu' ";
107  constexpr const char Acc[] = "'info::device_type::accelerator' ";
108  constexpr const char Suffix[] = "available.";
109  constexpr auto ReserveSize = sizeof(Prefix) + sizeof(Suffix) + sizeof(Acc);
110  Message.reserve(ReserveSize);
111  Message += Prefix;
112 
113  auto Selector =
114  DeviceSelectorInvocable.target<int (*)(const sycl::device &)>();
115  if ((Selector && *Selector == gpu_selector_v) ||
116  DeviceSelectorInvocable.target<sycl::gpu_selector>()) {
117  Message += Gpu;
118  } else if ((Selector && *Selector == cpu_selector_v) ||
119  DeviceSelectorInvocable.target<sycl::cpu_selector>()) {
120  Message += Cpu;
121  } else if ((Selector && *Selector == accelerator_selector_v) ||
122  DeviceSelectorInvocable.target<sycl::accelerator_selector>()) {
123  Message += Acc;
124  }
125  Message += Suffix;
126  throw exception(make_error_code(errc::runtime), Message);
127 }
128 
129 // select_device(selector)
130 __SYCL_EXPORT device
131 select_device(const DSelectorInvocableType &DeviceSelectorInvocable) {
132  std::vector<device> Devices = device::get_devices();
133 
134  return select_device(DeviceSelectorInvocable, Devices);
135 }
136 
137 // select_device(selector, context)
138 __SYCL_EXPORT device
139 select_device(const DSelectorInvocableType &DeviceSelectorInvocable,
140  const context &SyclContext) {
141  device SelectedDevice = select_device(DeviceSelectorInvocable);
142 
143  // Throw exception if selected device is not in context.
144  std::vector<device> Devices = SyclContext.get_devices();
145  if (std::find(Devices.begin(), Devices.end(), SelectedDevice) ==
146  Devices.end())
148  "Selected device is not in the given context.");
149 
150  return SelectedDevice;
151 }
152 
153 } // namespace detail
154 
155 // -------------- SYCL 2020
156 
163 
164 static void traceDeviceSelector(const std::string &DeviceType) {
165  bool ShouldTrace = detail::ur::trace();
166  if (ShouldTrace) {
167  std::cout << "SYCL_UR_TRACE: Requested device_type: " << DeviceType
168  << std::endl;
169  }
170 }
171 
172 __SYCL_EXPORT int default_selector_v(const device &dev) {
173  // The default selector doesn't reject any devices.
174  int Score = 0;
175 
176  traceDeviceSelector("info::device_type::automatic");
177 
178  if (dev.is_gpu())
179  Score += 500;
180 
181  if (dev.is_cpu())
182  Score += 300;
183 
184  // Since we deprecate SYCL_BE and SYCL_DEVICE_TYPE,
185  // we should not disallow accelerator to be chosen.
186  // But this device type gets the lowest heuristic point.
187  if (dev.is_accelerator())
188  Score += 75;
189 
190  // Add preference score.
191  Score += detail::getDevicePreference(dev);
192 
193  return Score;
194 }
195 
196 __SYCL_EXPORT int gpu_selector_v(const device &dev) {
197  int Score = detail::REJECT_DEVICE_SCORE;
198 
199  traceDeviceSelector("info::device_type::gpu");
200  if (dev.is_gpu()) {
201  Score = 1000;
202  Score += detail::getDevicePreference(dev);
203  }
204  return Score;
205 }
206 
207 __SYCL_EXPORT int cpu_selector_v(const device &dev) {
208  int Score = detail::REJECT_DEVICE_SCORE;
209 
210  traceDeviceSelector("info::device_type::cpu");
211  if (dev.is_cpu()) {
212  Score = 1000;
213  Score += detail::getDevicePreference(dev);
214  }
215  return Score;
216 }
217 
218 __SYCL_EXPORT int accelerator_selector_v(const device &dev) {
219  int Score = detail::REJECT_DEVICE_SCORE;
220 
221  traceDeviceSelector("info::device_type::accelerator");
222  if (dev.is_accelerator()) {
223  Score = 1000;
224  Score += detail::getDevicePreference(dev);
225  }
226  return Score;
227 }
228 
229 __SYCL_EXPORT detail::DSelectorInvocableType
230 aspect_selector(const std::vector<aspect> &RequireList,
231  const std::vector<aspect> &DenyList /* ={} */) {
232  return [=](const sycl::device &Dev) {
233  auto DevHas = [&](const aspect &Asp) -> bool { return Dev.has(Asp); };
234 
235  // All aspects from require list are required.
236  if (!std::all_of(RequireList.begin(), RequireList.end(), DevHas))
238 
239  // No aspect from deny list is allowed
240  if (std::any_of(DenyList.begin(), DenyList.end(), DevHas))
242 
243  if (RequireList.size() > 0) {
244  return 1000 + detail::getDevicePreference(Dev);
245  } else {
246  // No required aspects specified.
247  // SYCL 2020 4.6.1.1 "If no aspects are passed in, the generated selector
248  // behaves like default_selector."
249  return default_selector_v(Dev);
250  }
251  };
252 }
253 
254 // -------------- SYCL 1.2.1
255 
256 // SYCL 1.2.1 device_selector class and sub-classes
257 
258 device device_selector::select_device() const {
259  return detail::select_device([&](const device &dev) { return (*this)(dev); });
260 }
261 
262 int default_selector::operator()(const device &dev) const {
263  return default_selector_v(dev);
264 }
265 
266 int gpu_selector::operator()(const device &dev) const {
267  return gpu_selector_v(dev);
268 }
269 
270 int cpu_selector::operator()(const device &dev) const {
271  return cpu_selector_v(dev);
272 }
273 
275  return accelerator_selector_v(dev);
276 }
277 
278 namespace ext::oneapi {
279 
280 filter_selector::filter_selector(sycl::detail::string_view Input)
281  : impl(std::make_shared<detail::filter_selector_impl>(Input.data())) {}
282 
283 int filter_selector::operator()(const device &Dev) const {
284  return impl->operator()(Dev);
285 }
286 
287 void filter_selector::reset() const { impl->reset(); }
288 
289 // filter_selectors not "Callable"
290 // because of the requirement that the filter_selector "reset()" itself
291 // between invocations, the filter_selector operator() is not purely callable
292 // and cannot be used interchangeably as a SYCL2020 callable device selector.
293 // TODO: replace the FilterSelector subclass with something that
294 // doesn't pretend to be a device_selector, and instead is something that
295 // just returns a device (rather than a score).
296 // Then remove ! std::is_base_of_v<ext::oneapi::filter_selector, DeviceSelector>
297 // from device/platform/queue constructors
299  std::lock_guard<std::mutex> Guard(
300  sycl::detail::GlobalHandler::instance().getFilterMutex());
301 
303 
304  reset();
305 
306  return Result;
307 }
308 
309 } // namespace ext::oneapi
310 
311 namespace __SYCL2020_DEPRECATED("use 'ext::oneapi' instead") ONEAPI {
312 using namespace ext::oneapi;
313 filter_selector::filter_selector(sycl::detail::string_view Input)
314  : ext::oneapi::filter_selector(Input) {}
315 
316 int filter_selector::operator()(const device &Dev) const {
318 }
319 
321 
322 device filter_selector::select_device() const {
324 }
325 } // namespace __SYCL2020_DEPRECATED("use 'ext::oneapi' instead")ONEAPI
326 } // namespace _V1
327 } // 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:124
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:79
bool is_gpu() const
Check if device is a GPU device.
Definition: device.cpp:77
static std::vector< device > get_devices(info::device_type deviceType=info::device_type::all)
Query available SYCL devices.
Definition: device.cpp:53
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:215
bool is_cpu() const
Get instance of device.
Definition: device.cpp:75
filter_selector(const std::string &filter)
int operator()(const device &dev) const override
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
decltype(Obj::impl) const & getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:31
static int getDevicePreference(const device &Device)
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)
int default_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:65
Definition: access.hpp:18
bool any_of(const simd_mask< _Tp, _Abi > &) noexcept
bool all_of(const simd_mask< _Tp, _Abi > &) noexcept