Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SYCL 2020] Add new device selector API #531

Merged
merged 1 commit into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions include/hipSYCL/sycl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ class context
explicit context(async_handler handler = [](exception_list e) {
glue::default_async_handler(e);
}) {
default_selector selector;
this->init(handler, selector.select_device());
this->init(handler, detail::select_device(default_selector_v));
}

explicit context(
Expand Down
213 changes: 165 additions & 48 deletions include/hipSYCL/sycl/device_selector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,91 +33,208 @@
#include "device.hpp"

#include <limits>
#include <functional>
#include <type_traits>

namespace hipsycl {
namespace sycl {

namespace detail {

inline int select_gpu(const device& dev) {
if (dev.is_gpu()) {
// Would be good to prefer a device for which
// we have actually compiled kernel code, because,
// I don't know, a user might try to run kernels..
if (dev.hipSYCL_has_compiled_kernels())
return 2;
else
return 1;
}
return -1;
}

inline int select_accelerator(const device& dev) {
if(dev.is_accelerator()) {
if(dev.hipSYCL_has_compiled_kernels())
return 2;
else
return 1;
}
return -1;
}

inline int select_cpu(const device& dev) {
if(dev.is_cpu())
return 1;
return -1;
}

inline int select_host(const device& dev) {
return select_cpu(dev);
}

inline int select_default(const device& dev) {
#if defined(__HIPSYCL_ENABLE_CUDA_TARGET__) || \
defined(__HIPSYCL_ENABLE_HIP_TARGET__) || \
defined(__HIPSYCL_ENABLE_SPIRV_TARGET__)
// Add 2 to make sure that, if no GPU is found
if(!dev.is_cpu() && dev.hipSYCL_has_compiled_kernels()) {
// Prefer GPUs (or other accelerators) that have been targeted
// and have compiled kernels
return 2;
} else if(dev.is_cpu()) {
// Prefer CPU over GPUs that don't have compiled kernels
// and cannot run kernels.
return 1;
} else {
// Last option: GPUs without compiled kernels
// This should never be selected in practice because
// there's always a CPU device.
return 0;
}
#else
return select_host(dev);
#endif
}

template<class Selector>
device select_device(const Selector& s) {
auto devices = device::get_devices();
// There should always be at least a CPU device
assert(devices.size() > 0);

int best_score = std::numeric_limits<int>::min();
device candidate;
for (const device &d : devices) {
int current_score = s(d);
if (current_score > best_score) {
best_score = current_score;
candidate = d;
}
}
if (best_score < 0) {
throw sycl::runtime_error{"No matching device"};
}

return candidate;
}

template<class T>
struct is_device_selector {
static constexpr bool value =
std::is_convertible_v<T, std::function<int(const device &)>>;
};

template<class T>
inline constexpr bool is_device_selector_v = is_device_selector<T>::value;

}

/// Provided only for backwards-compatibility with SYCL 1.2.1
/// so users can still derive custom selectors from device_selector
class device_selector
{
public:
virtual ~device_selector(){};

device select_device() const {
auto devices = device::get_devices();
if (devices.size() == 0)
throw platform_error{"No available devices!"};

int best_score = std::numeric_limits<int>::min();
device candidate;
for (const device &d : devices) {
int current_score = (*this)(d);
if (current_score > best_score) {
best_score = current_score;
candidate = d;
}
}
return candidate;
return detail::select_device(*this);
}

virtual int operator()(const device& dev) const = 0;

};


class error_selector : public device_selector
{
/// Old SYCL 1.2.1 types are still required for backwards compatibility
/// Note: SYCL 2020 does not mandate how they are implemented - in
/// particular, they don't have to be derived from device_selector!
class error_selector {
public:
virtual ~error_selector(){}
virtual int operator()(const device& dev) const
{
int operator()(const device &dev) const {
throw unimplemented{"error_selector device selection invoked"};
}
};

class gpu_selector : public device_selector
{
class gpu_selector {
public:
virtual ~gpu_selector() {}
virtual int operator()(const device &dev) const {
if (dev.is_gpu()) {
// Would be good to prefer a device for which
// we have actually compiled kernel code, because,
// I don't know, a user might try to run kernels..
if (dev.hipSYCL_has_compiled_kernels())
return 2;
else
return 1;

}
return 0;
int operator()(const device &dev) const { return detail::select_gpu(dev); }
};

class accelerator_selector {
public:
int operator()(const device &dev) const {
return detail::select_accelerator(dev);
}
};

class cpu_selector : public device_selector
{
class cpu_selector {
public:
virtual ~cpu_selector() {}
virtual int operator()(const device &dev) const {
return dev.is_cpu();
int operator()(const device &dev) const { return detail::select_cpu(dev); }
};

class host_selector {
public:
int operator()(const device &dev) const { return detail::select_host(dev); }
};

class default_selector {
public:
int operator()(const device &dev) const {
return detail::select_default(dev);
}
};

using host_selector = cpu_selector;

#if defined(__HIPSYCL_ENABLE_CUDA_TARGET__) || \
defined(__HIPSYCL_ENABLE_HIP_TARGET__) || \
defined(__HIPSYCL_ENABLE_SPIRV_TARGET__)
using default_selector = gpu_selector;
#else
using default_selector = host_selector;
#endif
inline constexpr default_selector default_selector_v;
inline constexpr cpu_selector cpu_selector_v;
inline constexpr gpu_selector gpu_selector_v;
inline constexpr accelerator_selector accelerator_selector_v;

inline auto aspect_selector(const std::vector<aspect> &aspectList,
const std::vector<aspect> &denyList = {}) {

return [=](const device& dev) {
if(aspectList.empty() && denyList.empty())
return detail::select_default(dev);

for(aspect a : aspectList) {
if(!dev.has(a))
return -1;
}
for(aspect a : denyList) {
if(dev.has(a))
return -1;
}
return 1;
};
}

template <typename... aspectListTN>
auto aspect_selector(aspectListTN... aspectList) {
return [=](const device& dev) {
if(sizeof...(aspectList) == 0)
return detail::select_default(dev);

bool satisfies_all = (dev.has(aspectList) && ...);
if(satisfies_all)
return 1;
return -1;
};
}

template <aspect... aspectList>
auto aspect_selector() {
return aspect_selector(aspectList...);
}

inline device::device(const device_selector &deviceSelector) {
this->_device_id = deviceSelector.select_device()._device_id;
}




}
}

Expand Down
5 changes: 3 additions & 2 deletions include/hipSYCL/sycl/platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ class platform {
platform(rt::backend_id backend)
: _platform{backend, 0} {}

explicit platform(const device_selector &deviceSelector) {
auto dev = deviceSelector.select_device();
template<class DeviceSelector>
explicit platform(const DeviceSelector &deviceSelector) {
auto dev = detail::select_device(deviceSelector);
this->_platform = rt::platform_id{dev._device_id};
}

Expand Down
45 changes: 28 additions & 17 deletions include/hipSYCL/sycl/queue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,40 +111,46 @@ class queue : public detail::property_carrying_object

public:
explicit queue(const property_list &propList = {})
: queue{default_selector{},
: queue{default_selector_v,
[](exception_list e) { glue::default_async_handler(e); },
propList} {
assert(_default_hints.has_hint<rt::hints::bind_to_device>());
}

explicit queue(const async_handler &asyncHandler,
const property_list &propList = {})
: queue{default_selector{}, asyncHandler, propList} {
: queue{default_selector_v, asyncHandler, propList} {
assert(_default_hints.has_hint<rt::hints::bind_to_device>());
}

explicit queue(const device_selector &deviceSelector,
template <
class DeviceSelector,
std::enable_if_t<detail::is_device_selector_v<DeviceSelector>, int> = 0>
explicit queue(const DeviceSelector &deviceSelector,
const property_list &propList = {})
: detail::property_carrying_object{propList},
_ctx{deviceSelector.select_device()} {
: detail::property_carrying_object{propList}, _ctx{detail::select_device(
deviceSelector)} {

_handler = _ctx._impl->handler;

_default_hints.add_hint(rt::make_execution_hint<rt::hints::bind_to_device>(
deviceSelector.select_device()._device_id));
detail::select_device(deviceSelector)._device_id));

this->init();
}

explicit queue(const device_selector &deviceSelector,
template <
class DeviceSelector,
std::enable_if_t<detail::is_device_selector_v<DeviceSelector>, int> = 0>
explicit queue(const DeviceSelector &deviceSelector,
const async_handler &asyncHandler,
const property_list &propList = {})
: detail::property_carrying_object{propList},
_ctx{deviceSelector.select_device(), asyncHandler}, _handler{
asyncHandler} {
: detail::property_carrying_object{propList},
_ctx{detail::select_device(deviceSelector), asyncHandler},
_handler{asyncHandler} {

_default_hints.add_hint(rt::make_execution_hint<rt::hints::bind_to_device>(
deviceSelector.select_device()._device_id));
detail::select_device(deviceSelector)._device_id));

this->init();
}
Expand All @@ -171,18 +177,23 @@ class queue : public detail::property_carrying_object
this->init();
}

template <
class DeviceSelector,
std::enable_if_t<detail::is_device_selector_v<DeviceSelector>, int> = 0>
explicit queue(const context &syclContext,
const device_selector &deviceSelector,
const DeviceSelector &deviceSelector,
const property_list &propList = {})
: queue(syclContext, deviceSelector.select_device(), propList) {
}
: queue(syclContext, detail::select_device(deviceSelector), propList) {}

template <
class DeviceSelector,
std::enable_if_t<detail::is_device_selector_v<DeviceSelector>, int> = 0>
explicit queue(const context &syclContext,
const device_selector &deviceSelector,
const DeviceSelector &deviceSelector,
const async_handler &asyncHandler,
const property_list &propList = {})
: queue(syclContext, deviceSelector.select_device(), asyncHandler, propList) {
}
: queue(syclContext, detail::select_device(deviceSelector), asyncHandler,
propList) {}

explicit queue(const context &syclContext,
const device &syclDevice,
Expand Down