diff --git a/sycl/include/sycl/device.hpp b/sycl/include/sycl/device.hpp index 271daf37fc803..4c66109256f48 100644 --- a/sycl/include/sycl/device.hpp +++ b/sycl/include/sycl/device.hpp @@ -33,6 +33,13 @@ class device_impl; auto getDeviceComparisonLambda(); } // namespace detail +namespace ext { +namespace oneapi { +// Forward declaration +class filter_selector; +} // namespace oneapi +} // namespace ext + /// The SYCL device class encapsulates a single SYCL device on which kernels /// may be executed. /// @@ -53,9 +60,20 @@ class __SYCL_EXPORT device { /// Constructs a SYCL device instance using the device selected /// by the DeviceSelector provided. /// - /// \param DeviceSelector SYCL device selector to be used (see 4.6.1.1). + /// \param DeviceSelector SYCL 1.2.1 device_selector to be used (see 4.6.1.1). explicit device(const device_selector &DeviceSelector); +#if __cplusplus >= 201703L + /// Constructs a SYCL device instance using the device + /// identified by the device selector provided. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + template > + explicit device(const DeviceSelector &deviceSelector) + : device(detail::select_device(deviceSelector)) {} +#endif + bool operator==(const device &rhs) const { return impl == rhs.impl; } bool operator!=(const device &rhs) const { return !(*this == rhs); } diff --git a/sycl/include/sycl/device_selector.hpp b/sycl/include/sycl/device_selector.hpp index 85bae514b1fba..2565ae18e7be6 100644 --- a/sycl/include/sycl/device_selector.hpp +++ b/sycl/include/sycl/device_selector.hpp @@ -19,16 +19,19 @@ namespace sycl { // Forward declarations class device; -/// The device_selector class provides ability to choose the best SYCL device -/// based on heuristics specified by the user. +namespace ext { +namespace oneapi { +class filter_selector; +} +} // namespace ext + +/// The SYCL 1.2.1 device_selector class provides ability to choose the +/// best SYCL device based on heuristics specified by the user. /// /// \sa device /// /// \ingroup sycl_api_dev_sel class __SYCL_EXPORT device_selector { -protected: - // SYCL 1.2.1 defines a negative score to reject a device from selection - static constexpr int REJECT_DEVICE_SCORE = -1; public: virtual ~device_selector() = default; @@ -87,5 +90,33 @@ class __SYCL_EXPORT host_selector : public device_selector { public: int operator()(const device &dev) const override; }; + +namespace detail { + +// SYCL 2020 section 4.6.1.1 defines a negative score to reject a device from +// selection +static constexpr int REJECT_DEVICE_SCORE = -1; + +using DSelectorInvocableType = std::function; + +#if __cplusplus >= 201703L + +// Enable if DeviceSelector callable has matching signature, but +// exclude if descended from filter_selector which is not purely callable. +// See [FilterSelector not Callable] in device_selector.cpp +template +using EnableIfDeviceSelectorInvocable = std::enable_if_t< + std::is_invocable_r_v && + !std::is_base_of_v>; +#endif + +__SYCL_EXPORT device +select_device(const DSelectorInvocableType &DeviceSelectorInvocable); + +__SYCL_EXPORT device +select_device(const DSelectorInvocableType &DeviceSelectorInvocable, + const context &SyclContext); + +} // namespace detail } // namespace sycl } // __SYCL_INLINE_NAMESPACE(cl) diff --git a/sycl/include/sycl/platform.hpp b/sycl/include/sycl/platform.hpp index 231070996101a..94265450e66ba 100644 --- a/sycl/include/sycl/platform.hpp +++ b/sycl/include/sycl/platform.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include // 4.6.2 Platform class @@ -31,6 +32,12 @@ auto get_native(const SyclObjectT &Obj) namespace detail { class platform_impl; } +namespace ext { +namespace oneapi { +// Forward declaration +class filter_selector; +} // namespace oneapi +} // namespace ext /// Encapsulates a SYCL platform on which kernels may be executed. /// @@ -50,15 +57,26 @@ class __SYCL_EXPORT platform { explicit platform(cl_platform_id PlatformId); #endif - /// Constructs a SYCL platform instance using device selector. + /// Constructs a SYCL platform instance using a device_selector. /// /// One of the SYCL devices that is associated with the constructed SYCL /// platform instance must be the SYCL device that is produced from the /// provided device selector. /// - /// \param DeviceSelector is an instance of SYCL device_selector. + /// \param DeviceSelector is an instance of a SYCL 1.2.1 device_selector explicit platform(const device_selector &DeviceSelector); +#if __cplusplus >= 201703L + /// Constructs a SYCL platform instance using the platform of the device + /// identified by the device selector provided. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + template > + explicit platform(const DeviceSelector &deviceSelector) + : platform(detail::select_device(deviceSelector)) {} +#endif + platform(const platform &rhs) = default; platform(platform &&rhs) = default; @@ -141,6 +159,8 @@ class __SYCL_EXPORT platform { std::shared_ptr impl; platform(std::shared_ptr impl) : impl(impl) {} + platform(const device &Device); + template friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj); template diff --git a/sycl/include/sycl/queue.hpp b/sycl/include/sycl/queue.hpp index 532112d5363d7..3f3f135e653d9 100644 --- a/sycl/include/sycl/queue.hpp +++ b/sycl/include/sycl/queue.hpp @@ -28,6 +28,7 @@ #define __STDC_FORMAT_MACROS 1 #endif #include +#include #include // having _TWO_ mid-param #ifdefs makes the functions very difficult to read. @@ -123,10 +124,68 @@ class __SYCL_EXPORT queue { queue(const async_handler &AsyncHandler, const property_list &PropList = {}) : queue(default_selector(), AsyncHandler, PropList) {} +#if __cplusplus >= 201703L + /// Constructs a SYCL queue instance using the device identified by the + /// device selector provided. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + /// \param AsyncHandler is a SYCL asynchronous exception handler. + /// \param PropList is a list of properties for queue construction. + template > + explicit queue(const DeviceSelector &deviceSelector, + const async_handler &AsyncHandler, + const property_list &PropList = {}) + : queue(detail::select_device(deviceSelector), AsyncHandler, PropList) {} + + /// Constructs a SYCL queue instance using the device identified by the + /// device selector provided. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + /// \param PropList is a list of properties for queue construction. + template > + explicit queue(const DeviceSelector &deviceSelector, + const property_list &PropList = {}) + : queue(detail::select_device(deviceSelector), async_handler{}, + PropList) {} + + /// Constructs a SYCL queue instance using the device identified by the + /// device selector provided. + /// \param SyclContext is an instance of SYCL context. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + /// \param PropList is a list of properties for queue construction. + template > + explicit queue(const context &syclContext, + const DeviceSelector &deviceSelector, + const property_list &propList = {}) + : queue(syclContext, detail::select_device(deviceSelector, syclContext), + propList) {} + + /// Constructs a SYCL queue instance using the device identified by the + /// device selector provided. + /// \param SyclContext is an instance of SYCL context. + /// \param DeviceSelector is SYCL 2020 Device Selector, a simple callable that + /// takes a device and returns an int + /// \param AsyncHandler is a SYCL asynchronous exception handler. + /// \param PropList is a list of properties for queue construction. + template > + explicit queue(const context &syclContext, + const DeviceSelector &deviceSelector, + const async_handler &AsyncHandler, + const property_list &propList = {}) + : queue(syclContext, detail::select_device(deviceSelector, syclContext), + AsyncHandler, propList) {} + +#endif + /// Constructs a SYCL queue instance using the device returned by the /// DeviceSelector provided. /// - /// \param DeviceSelector is an instance of SYCL device selector. + /// \param DeviceSelector is an instance of a SYCL 1.2.1 device_selector. /// \param PropList is a list of properties for queue construction. queue(const device_selector &DeviceSelector, const property_list &PropList = {}) @@ -135,7 +194,7 @@ class __SYCL_EXPORT queue { /// Constructs a SYCL queue instance with an async_handler using the device /// returned by the DeviceSelector provided. /// - /// \param DeviceSelector is an instance of SYCL device selector. + /// \param DeviceSelector is an instance of SYCL 1.2.1 device_selector. /// \param AsyncHandler is a SYCL asynchronous exception handler. /// \param PropList is a list of properties for queue construction. queue(const device_selector &DeviceSelector, diff --git a/sycl/source/device_selector.cpp b/sycl/source/device_selector.cpp index f0e1b99f22214..33ddac5006d5e 100644 --- a/sycl/source/device_selector.cpp +++ b/sycl/source/device_selector.cpp @@ -27,6 +27,8 @@ __SYCL_INLINE_NAMESPACE(cl) { namespace sycl { +namespace detail { + // SYCL_DEVICE_FILTER doesn't need to be considered in the device preferences // as it filters the device list returned by device::get_devices itself, so // only matching devices will be scored. @@ -50,36 +52,50 @@ static int getDevicePreference(const device &Device) { return Score; } -device device_selector::select_device() const { - std::vector devices = device::get_devices(); - int score = REJECT_DEVICE_SCORE; +static void traceDeviceSelection(const device &Device, int Score, bool Chosen) { + bool shouldTrace = false; + if (Chosen) { + shouldTrace = detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_BASIC); + } else { + shouldTrace = detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_ALL); + } + if (shouldTrace) { + std::string PlatformName = Device.get_info() + .get_info(); + std::string DeviceName = Device.get_info(); + auto selectionMsg = Chosen ? "Selected device: -> final score = " + : "Candidate device: -> score = "; + + std::cout << "SYCL_PI_TRACE[all]: " << selectionMsg << Score + << ((Score < 0) ? " (REJECTED)" : "") << std::endl + << "SYCL_PI_TRACE[all]: " + << " platform: " << PlatformName << std::endl + << "SYCL_PI_TRACE[all]: " + << " device: " << DeviceName << std::endl; + } +} + +device select_device(DSelectorInvocableType DeviceSelectorInvocable, + std::vector &Devices) { + int score = detail::REJECT_DEVICE_SCORE; const device *res = nullptr; - for (const auto &dev : devices) { - int dev_score = (*this)(dev); - - if (detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_ALL)) { - std::string PlatformName = dev.get_info() - .get_info(); - std::string DeviceName = dev.get_info(); - std::cout << "SYCL_PI_TRACE[all]: " - << "select_device(): -> score = " << dev_score - << ((dev_score < 0) ? " (REJECTED)" : "") << std::endl - << "SYCL_PI_TRACE[all]: " - << " platform: " << PlatformName << std::endl - << "SYCL_PI_TRACE[all]: " - << " device: " << DeviceName << std::endl; - } + for (const auto &dev : Devices) { + int dev_score = DeviceSelectorInvocable(dev); + + traceDeviceSelection(dev, dev_score, false); // A negative score means that a device must not be selected. if (dev_score < 0) continue; - // SYCL spec says: "If more than one device receives the high score then + // Section 4.6 of SYCL 1.2.1 spec: + // "If more than one device receives the high score then // one of those tied devices will be returned, but which of the devices // from the tied set is to be returned is not defined". So use the device // preference score to resolve ties, this is necessary for custom_selectors // that may not already include device preference in their scoring. + if ((score < dev_score) || ((score == dev_score) && (getDevicePreference(*res) < getDevicePreference(dev)))) { @@ -89,17 +105,8 @@ device device_selector::select_device() const { } if (res != nullptr) { - if (detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_BASIC)) { - std::string PlatformName = res->get_info() - .get_info(); - std::string DeviceName = res->get_info(); - std::cout << "SYCL_PI_TRACE[all]: " - << "Selected device ->" << std::endl - << "SYCL_PI_TRACE[all]: " - << " platform: " << PlatformName << std::endl - << "SYCL_PI_TRACE[all]: " - << " device: " << DeviceName << std::endl; - } + traceDeviceSelection(*res, score, true); + return *res; } @@ -107,6 +114,29 @@ device device_selector::select_device() const { PI_ERROR_DEVICE_NOT_FOUND); } +// select_device(selector) +__SYCL_EXPORT device +select_device(const DSelectorInvocableType &DeviceSelectorInvocable) { + std::vector Devices = device::get_devices(); + + return select_device(DeviceSelectorInvocable, Devices); +} + +// select_device(selector, context) +__SYCL_EXPORT device +select_device(const DSelectorInvocableType &DeviceSelectorInvocable, + const context &SyclContext) { + std::vector devices = SyclContext.get_devices(); + + return select_device(DeviceSelectorInvocable, devices); +} + +} // namespace detail + +device device_selector::select_device() const { + return detail::select_device([&](const device &dev) { return (*this)(dev); }); +} + /// Devices of different kinds are prioritized in the following order: /// 1. GPU /// 2. CPU @@ -135,47 +165,47 @@ int default_selector::operator()(const device &dev) const { Score += 75; // Add preference score. - Score += getDevicePreference(dev); + Score += detail::getDevicePreference(dev); return Score; } int gpu_selector::operator()(const device &dev) const { - int Score = REJECT_DEVICE_SCORE; + int Score = detail::REJECT_DEVICE_SCORE; if (dev.is_gpu()) { Score = 1000; - Score += getDevicePreference(dev); + Score += detail::getDevicePreference(dev); } return Score; } int cpu_selector::operator()(const device &dev) const { - int Score = REJECT_DEVICE_SCORE; + int Score = detail::REJECT_DEVICE_SCORE; if (dev.is_cpu()) { Score = 1000; - Score += getDevicePreference(dev); + Score += detail::getDevicePreference(dev); } return Score; } int accelerator_selector::operator()(const device &dev) const { - int Score = REJECT_DEVICE_SCORE; + int Score = detail::REJECT_DEVICE_SCORE; if (dev.is_accelerator()) { Score = 1000; - Score += getDevicePreference(dev); + Score += detail::getDevicePreference(dev); } return Score; } int host_selector::operator()(const device &dev) const { - int Score = REJECT_DEVICE_SCORE; + int Score = detail::REJECT_DEVICE_SCORE; if (dev.is_host()) { Score = 1000; - Score += getDevicePreference(dev); + Score += detail::getDevicePreference(dev); } return Score; } @@ -192,6 +222,15 @@ int filter_selector::operator()(const device &Dev) const { void filter_selector::reset() const { impl->reset(); } +// filter_selectors not "Callable" +// because of the requirement that the filter_selector "reset()" itself +// between invocations, the filter_selector operator() is not purely callable +// and cannot be used interchangeably as a SYCL2020 callable device selector. +// TODO: replace the FilterSelector subclass with something that +// doesn't pretend to be a device_selector, and instead is something that +// just returns a device (rather than a score). +// Then remove ! std::is_base_of_v +// from device/platform/queue constructors device filter_selector::select_device() const { std::lock_guard Guard( sycl::detail::GlobalHandler::instance().getFilterMutex()); @@ -221,5 +260,6 @@ namespace __SYCL2020_DEPRECATED("use 'ext::oneapi' instead") ONEAPI { return ext::oneapi::filter_selector::select_device(); } } // namespace ONEAPI + } // namespace sycl } // __SYCL_INLINE_NAMESPACE(cl) diff --git a/sycl/source/platform.cpp b/sycl/source/platform.cpp index 6483251d11494..3cf9e083cd4a6 100644 --- a/sycl/source/platform.cpp +++ b/sycl/source/platform.cpp @@ -27,6 +27,9 @@ platform::platform(cl_platform_id PlatformId) { detail::RT::getPlugin()); } +// protected constructor for internal use +platform::platform(const device &Device) { *this = Device.get_platform(); } + platform::platform(const device_selector &dev_selector) { *this = dev_selector.select_device().get_platform(); } diff --git a/sycl/test/abi/sycl_symbols_linux.dump b/sycl/test/abi/sycl_symbols_linux.dump index 9fa75437b35d4..149f3f243e254 100644 --- a/sycl/test/abi/sycl_symbols_linux.dump +++ b/sycl/test/abi/sycl_symbols_linux.dump @@ -3880,6 +3880,8 @@ _ZN2cl4sycl6detail13MemoryManager8allocateESt10shared_ptrINS1_12context_implEEPN _ZN2cl4sycl6detail13MemoryManager8copy_usmEPKvSt10shared_ptrINS1_10queue_implEEmPvSt6vectorIP9_pi_eventSaISB_EEPSB_ _ZN2cl4sycl6detail13MemoryManager8fill_usmEPvSt10shared_ptrINS1_10queue_implEEmiSt6vectorIP9_pi_eventSaIS9_EEPS9_ _ZN2cl4sycl6detail13make_platformEmNS0_7backendE +_ZN2cl4sycl6detail13select_deviceERKSt8functionIFiRKNS0_6deviceEEE +_ZN2cl4sycl6detail13select_deviceERKSt8functionIFiRKNS0_6deviceEEERKNS0_7contextE _ZN2cl4sycl6detail14getBorderColorENS0_19image_channel_orderE _ZN2cl4sycl6detail14host_half_impl4halfC1ERKf _ZN2cl4sycl6detail14host_half_impl4halfC2ERKf @@ -4043,9 +4045,11 @@ _ZN2cl4sycl7samplerC2EP11_cl_samplerRKNS0_7contextE _ZN2cl4sycl8platform13get_platformsEv _ZN2cl4sycl8platformC1EP15_cl_platform_id _ZN2cl4sycl8platformC1ERKNS0_15device_selectorE +_ZN2cl4sycl8platformC1ERKNS0_6deviceE _ZN2cl4sycl8platformC1Ev _ZN2cl4sycl8platformC2EP15_cl_platform_id _ZN2cl4sycl8platformC2ERKNS0_15device_selectorE +_ZN2cl4sycl8platformC2ERKNS0_6deviceE _ZN2cl4sycl8platformC2Ev _ZN2cl4sycl9exceptionC1ENS0_7contextESt10error_code _ZN2cl4sycl9exceptionC1ENS0_7contextESt10error_codePKc diff --git a/sycl/test/abi/sycl_symbols_windows.dump b/sycl/test/abi/sycl_symbols_windows.dump index d0d988ebd5996..b076b148bd4e7 100644 --- a/sycl/test/abi/sycl_symbols_windows.dump +++ b/sycl/test/abi/sycl_symbols_windows.dump @@ -401,6 +401,7 @@ ??0kernel_id@sycl@cl@@AEAA@PEBD@Z ??0kernel_id@sycl@cl@@QEAA@$$QEAV012@@Z ??0kernel_id@sycl@cl@@QEAA@AEBV012@@Z +??0platform@sycl@cl@@AEAA@AEBVdevice@12@@Z ??0platform@sycl@cl@@AEAA@V?$shared_ptr@Vplatform_impl@detail@sycl@cl@@@std@@@Z ??0platform@sycl@cl@@QEAA@$$QEAV012@@Z ??0platform@sycl@cl@@QEAA@AEBV012@@Z @@ -953,7 +954,6 @@ ?Ordered@__host_std@cl@@YAHVhalf@half_impl@detail@sycl@2@0@Z ?PushBack@exception_list@sycl@cl@@AEAAX$$QEAVexception_ptr@std@@@Z ?PushBack@exception_list@sycl@cl@@AEAAXAEBVexception_ptr@std@@@Z -?REJECT_DEVICE_SCORE@device_selector@sycl@cl@@1HB ?RangeRoundingTrace@handler@sycl@cl@@AEAA_NXZ ?SignBitSet@__host_std@cl@@YA?AV?$vec@F$00@sycl@2@V?$vec@Vhalf@half_impl@detail@sycl@cl@@$00@42@@Z ?SignBitSet@__host_std@cl@@YA?AV?$vec@F$01@sycl@2@V?$vec@Vhalf@half_impl@detail@sycl@cl@@$01@42@@Z @@ -3931,6 +3931,8 @@ ?select@__host_std@cl@@YA_J_J0_K@Z ?select@__host_std@cl@@YA_K_K00@Z ?select@__host_std@cl@@YA_K_K0_J@Z +?select_device@detail@sycl@cl@@YA?AVdevice@23@AEBV?$function@$$A6AHAEBVdevice@sycl@cl@@@Z@std@@@Z +?select_device@detail@sycl@cl@@YA?AVdevice@23@AEBV?$function@$$A6AHAEBVdevice@sycl@cl@@@Z@std@@AEBVcontext@23@@Z ?select_device@device_selector@sycl@cl@@UEBA?AVdevice@23@XZ ?select_device@filter_selector@ONEAPI@sycl@cl@@UEBA?AVdevice@34@XZ ?select_device@filter_selector@oneapi@ext@sycl@cl@@UEBA?AVdevice@45@XZ diff --git a/sycl/unittests/helpers/PiMock.hpp b/sycl/unittests/helpers/PiMock.hpp index b91208612d227..ab5f316b67471 100644 --- a/sycl/unittests/helpers/PiMock.hpp +++ b/sycl/unittests/helpers/PiMock.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include