diff --git a/sycl/doc/EnvironmentVariables.md b/sycl/doc/EnvironmentVariables.md index cb9446ff3de11..c80ae04cf71df 100644 --- a/sycl/doc/EnvironmentVariables.md +++ b/sycl/doc/EnvironmentVariables.md @@ -53,25 +53,25 @@ subject to change. Do not rely on these variables in production code. This environment variable limits the SYCL RT to use only a subset of the system's devices. Setting this environment variable affects all of the device query functions (`platform::get_devices()` and `platform::get_platforms()`) and all of the device selectors. -The value of this environment variable is a comma separated list of filters, where each filter is a triple of the form "`backend:device_type:device_num`" (without the quotes). Each element of the triple is optional, but each filter must have at least one value. Possible values of "backend" are: -- host +The value of this environment variable is a comma separated list of filters, where each filter is a triple of the form "`backend`:`device_type`:`device_num`" (without the quotes). Each element of the triple is optional, but each filter must have at least one value. Possible values of `backend` are: +- `host` - `level_zero` -- opencl -- cuda -- \* +- `opencl` +- `cuda` +- `*` -Possible values of "`device_type`" are: -- host -- cpu -- gpu -- acc -- \* +Possible values of `device_type` are: +- `host` +- `cpu` +- `gpu` +- `acc` +- `*` -`Device_num` is an integer that indexes the enumeration of devices from the sycl-ls utility tool, where the first device in that enumeration has index zero in each backend. For example, `SYCL_DEVICE_FILTER`=2 will return all devices with index '2' from all different backends. If multiple devices satisfy this device number (e.g., GPU and CPU devices can be assigned device number '2'), then default_selector will choose the device with the highest heuristic point. +`device_num` is an integer that indexes the enumeration of devices from the sycl-ls utility tool, where the first device in that enumeration has index zero in each backend. For example, `SYCL_DEVICE_FILTER=2` will return all devices with index '2' from all different backends. If multiple devices satisfy this device number (e.g., GPU and CPU devices can be assigned device number '2'), then default_selector will choose the device with the highest heuristic point. When `SYCL_DEVICE_ALLOWLIST` is set, it is applied before enumerating devices and affects `device_num` values. -Assuming a filter has all three elements of the triple, it selects only those devices that come from the given backend, have the specified device type, AND have the given device index. If more than one filter is specified, the RT is restricted to the union of devices selected by all filters. The RT does not include the "host" backend and the host device automatically unless one of the filters explicitly specifies the "host" device type. Therefore, `SYCL_DEVICE_FILTER`=host should be set to enforce SYCL to use the host device only. +Assuming a filter has all three elements of the triple, it selects only those devices that come from the given backend, have the specified device type, AND have the given device index. If more than one filter is specified, the RT is restricted to the union of devices selected by all filters. The RT does not include the `host` backend and the `host` device automatically unless one of the filters explicitly specifies the `host` device type. Therefore, `SYCL_DEVICE_FILTER=host` should be set to enforce SYCL to use the `host` device only. -Note that all device selectors will throw an exception if the filtered list of devices does not include a device that satisfies the selector. For instance, `SYCL_DEVICE_FILTER`=cpu,level_zero will cause host_selector() to throw an exception. `SYCL_DEVICE_FILTER` also limits loading only specified plugins into the SYCL RT. In particular, `SYCL_DEVICE_FILTER`=level_zero will cause the cpu_selector to throw an exception since SYCL RT will only load the level_zero backend which does not support any CPU devices at this time. When multiple devices satisfy the filter (e..g, `SYCL_DEVICE_FILTER`=gpu), only one of them will be selected. +Note that all device selectors will throw an exception if the filtered list of devices does not include a device that satisfies the selector. For instance, `SYCL_DEVICE_FILTER=cpu,level_zero` will cause `host_selector()` to throw an exception. `SYCL_DEVICE_FILTER` also limits loading only specified plugins into the SYCL RT. In particular, `SYCL_DEVICE_FILTER=level_zero` will cause the `cpu_selector` to throw an exception since SYCL RT will only load the `level_zero` backend which does not support any CPU devices at this time. When multiple devices satisfy the filter (e..g, `SYCL_DEVICE_FILTER=gpu`), only one of them will be selected. ### `SYCL_PRINT_EXECUTION_GRAPH` Options diff --git a/sycl/include/CL/sycl/detail/device_filter.hpp b/sycl/include/CL/sycl/detail/device_filter.hpp index 746f7714e21a0..813c22282fb14 100644 --- a/sycl/include/CL/sycl/detail/device_filter.hpp +++ b/sycl/include/CL/sycl/detail/device_filter.hpp @@ -82,6 +82,9 @@ inline std::ostream &operator<<(std::ostream &Out, return Out; } +std::vector tokenize(const std::string &Filter, + const std::string &Delim); + } // namespace detail } // namespace sycl } // __SYCL_INLINE_NAMESPACE(cl) diff --git a/sycl/source/detail/config.hpp b/sycl/source/detail/config.hpp index b19152c912b1f..42acec1e1e61c 100644 --- a/sycl/source/detail/config.hpp +++ b/sycl/source/detail/config.hpp @@ -186,11 +186,11 @@ static const std::array, 5> // Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST static const std::array, 6> SyclBeMap = { - {{"host", backend::host}, - {"opencl", backend::opencl}, + {{"opencl", backend::opencl}, {"level_zero", backend::level_zero}, {"cuda", backend::cuda}, {"rocm", backend::rocm}, + {"host", backend::host}, {"*", backend::all}}}; template <> class SYCLConfig { diff --git a/sycl/source/detail/device_filter.cpp b/sycl/source/detail/device_filter.cpp index 3b0847d105571..49fa95c6921a9 100644 --- a/sycl/source/detail/device_filter.cpp +++ b/sycl/source/detail/device_filter.cpp @@ -17,60 +17,76 @@ __SYCL_INLINE_NAMESPACE(cl) { namespace sycl { namespace detail { +std::vector tokenize(const std::string &Filter, + const std::string &Delim) { + std::vector Tokens; + size_t Pos = 0; + std::string Input = Filter; + std::string Tok; + + while ((Pos = Input.find(Delim)) != std::string::npos) { + Tok = Input.substr(0, Pos); + Input.erase(0, Pos + Delim.length()); + + if (!Tok.empty()) { + Tokens.push_back(std::move(Tok)); + } + } + + // Add remainder + if (!Input.empty()) + Tokens.push_back(std::move(Input)); + + return Tokens; +} + device_filter::device_filter(const std::string &FilterString) { - size_t Cursor = 0; - size_t ColonPos = 0; - auto findElement = [&](auto Element) { - size_t Found = FilterString.find(Element.first, Cursor); + std::vector Tokens = tokenize(FilterString, ":"); + size_t I = 0; + + auto FindElement = [&](auto Element) { + size_t Found = Tokens[I].find(Element.first); if (Found == std::string::npos) return false; - Cursor = Found; return true; }; // Handle the optional 1st field of the filter, backend // Check if the first entry matches with a known backend type auto It = - std::find_if(std::begin(SyclBeMap), std::end(SyclBeMap), findElement); + std::find_if(std::begin(SyclBeMap), std::end(SyclBeMap), FindElement); // If no match is found, set the backend type backend::all // which actually means 'any backend' will be a match. if (It == SyclBeMap.end()) Backend = backend::all; else { Backend = It->second; - ColonPos = FilterString.find(":", Cursor); - if (ColonPos != std::string::npos) - Cursor = ColonPos + 1; - else - Cursor = Cursor + It->first.size(); + I++; } + // Handle the optional 2nd field of the filter - device type. // Check if the 2nd entry matches with any known device type. - if (Cursor >= FilterString.size()) { + if (I >= Tokens.size()) { DeviceType = info::device_type::all; } else { auto Iter = std::find_if(std::begin(SyclDeviceTypeMap), - std::end(SyclDeviceTypeMap), findElement); + std::end(SyclDeviceTypeMap), FindElement); // If no match is found, set device_type 'all', // which actually means 'any device_type' will be a match. if (Iter == SyclDeviceTypeMap.end()) DeviceType = info::device_type::all; else { DeviceType = Iter->second; - ColonPos = FilterString.find(":", Cursor); - if (ColonPos != std::string::npos) - Cursor = ColonPos + 1; - else - Cursor = Cursor + Iter->first.size(); + I++; } } // Handle the optional 3rd field of the filter, device number // Try to convert the remaining string to an integer. // If succeessful, the converted integer is the desired device num. - if (Cursor < FilterString.size()) { + if (I < Tokens.size()) { try { - DeviceNum = stoi(FilterString.substr(Cursor)); + DeviceNum = stoi(Tokens[I]); HasDeviceNum = true; } catch (...) { std::string Message = diff --git a/sycl/source/detail/filter_selector_impl.cpp b/sycl/source/detail/filter_selector_impl.cpp index a1e35d838b276..fa78bcf55d12e 100644 --- a/sycl/source/detail/filter_selector_impl.cpp +++ b/sycl/source/detail/filter_selector_impl.cpp @@ -24,35 +24,12 @@ namespace sycl { namespace ONEAPI { namespace detail { -std::vector tokenize(const std::string &Filter, - const std::string &Delim) { - std::vector Tokens; - size_t Pos = 0; - std::string Input = Filter; - std::string Tok; - - while ((Pos = Input.find(Delim)) != std::string::npos) { - Tok = Input.substr(0, Pos); - Input.erase(0, Pos + Delim.length()); - - if (!Tok.empty()) { - Tokens.push_back(std::move(Tok)); - } - } - - // Add remainder - if (!Input.empty()) - Tokens.push_back(std::move(Input)); - - return Tokens; -} - filter create_filter(const std::string &Input) { filter Result; constexpr auto Error = "Invalid filter string! Valid strings conform to " "BE:DeviceType:DeviceNum, where any are optional"; - std::vector Tokens = tokenize(Input, ":"); + std::vector Tokens = sycl::detail::tokenize(Input, ":"); std::regex IntegerExpr("[[:digit:]]+"); // There should only be up to 3 tokens. @@ -106,7 +83,7 @@ filter create_filter(const std::string &Input) { filter_selector_impl::filter_selector_impl(const std::string &Input) : mFilters(), mRanker(), mNumDevicesSeen(0), mMatchFound(false) { - std::vector Filters = detail::tokenize(Input, ","); + std::vector Filters = sycl::detail::tokenize(Input, ","); mNumTotalDevices = device::get_devices().size(); for (const std::string &Filter : Filters) { diff --git a/sycl/source/detail/global_handler.cpp b/sycl/source/detail/global_handler.cpp index 5b3647c6c920d..c60086735ebc4 100644 --- a/sycl/source/detail/global_handler.cpp +++ b/sycl/source/detail/global_handler.cpp @@ -52,8 +52,9 @@ ProgramManager &GlobalHandler::getProgramManager() { Sync &GlobalHandler::getSync() { return getOrCreate(MSync); } -std::vector &GlobalHandler::getPlatformCache() { - return getOrCreate(MPlatformCache); +std::map> & +GlobalHandler::getPlatformDeviceCache() { + return getOrCreate(MPlatformDeviceCache); } std::mutex &GlobalHandler::getPlatformMapMutex() { @@ -79,7 +80,7 @@ void shutdown() { // First, release resources, that may access plugins. GlobalHandler::instance().MScheduler.Inst.reset(nullptr); GlobalHandler::instance().MProgramManager.Inst.reset(nullptr); - GlobalHandler::instance().MPlatformCache.Inst.reset(nullptr); + GlobalHandler::instance().MPlatformDeviceCache.Inst.reset(nullptr); // Call to GlobalHandler::instance().getPlugins() initializes plugins. If // user application has loaded SYCL runtime, and never called any APIs, diff --git a/sycl/source/detail/global_handler.hpp b/sycl/source/detail/global_handler.hpp index 9e3e3305c1f19..3d43367078ea3 100644 --- a/sycl/source/detail/global_handler.hpp +++ b/sycl/source/detail/global_handler.hpp @@ -11,6 +11,7 @@ #include #include +#include #include __SYCL_INLINE_NAMESPACE(cl) { @@ -22,8 +23,10 @@ class ProgramManager; class Sync; class plugin; class device_filter_list; +class device_impl; using PlatformImplPtr = std::shared_ptr; +using DeviceImplPtr = std::shared_ptr; /// Wrapper class for global data structures with non-trivial destructors. /// @@ -51,7 +54,8 @@ class GlobalHandler { Scheduler &getScheduler(); ProgramManager &getProgramManager(); Sync &getSync(); - std::vector &getPlatformCache(); + std::map> & + getPlatformDeviceCache(); std::mutex &getPlatformMapMutex(); std::mutex &getFilterMutex(); std::vector &getPlugins(); @@ -77,7 +81,8 @@ class GlobalHandler { InstWithLock MScheduler; InstWithLock MProgramManager; InstWithLock MSync; - InstWithLock> MPlatformCache; + InstWithLock>> + MPlatformDeviceCache; InstWithLock MPlatformMapMutex; InstWithLock MFilterMutex; InstWithLock> MPlugins; diff --git a/sycl/source/detail/pi.cpp b/sycl/source/detail/pi.cpp index 29172be0891b8..ff74f168a4414 100644 --- a/sycl/source/detail/pi.cpp +++ b/sycl/source/detail/pi.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -361,6 +363,310 @@ bool trace(TraceLevel Level) { return (TraceLevelMask & Level) == Level; } +static bool IsBannedPlatform(const platform &Platform) { + // The NVIDIA OpenCL platform is currently not compatible with DPC++ + // since it is only 1.2 but gets selected by default in many systems + // There is also no support on the PTX backend for OpenCL consumption, + // and there have been some internal reports. + // To avoid problems on default users and deployment of DPC++ on platforms + // where CUDA is available, the NVidiaOpenCL support is disabled. + // + auto IsNVIDIAOpenCL = [](const platform &Platform) { + if (Platform.is_host()) + return false; + + const bool IsCUDA = Platform.get_info().find( + "NVIDIA CUDA") != std::string::npos; + const auto Backend = + detail::getSyclObjImpl(Platform)->getPlugin().getBackend(); + const bool IsCUDAOCL = (IsCUDA && Backend == backend::opencl); + if (detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_ALL) && IsCUDAOCL) { + std::cout << "SYCL_PI_TRACE[all]: " + << "NVIDIA CUDA OpenCL platform found but is not compatible." + << std::endl; + } + return IsCUDAOCL; + }; + return IsNVIDIAOpenCL(Platform); +} + +std::string getAllowListValue(const std::string &AllowList, size_t &Pos, + unsigned long int Size) { + size_t Prev = Pos; + if ((Pos = AllowList.find("{{", Pos)) == std::string::npos) { + throw sycl::runtime_error("Malformed syntax in SYCL_DEVICE_ALLOWLIST", + PI_INVALID_VALUE); + } + if (Pos > Prev + Size) { + throw sycl::runtime_error("Malformed syntax in SYCL_DEVICE_ALLOWLIST", + PI_INVALID_VALUE); + } + + Pos = Pos + 2; + size_t Start = Pos; + if ((Pos = AllowList.find("}}", Pos)) == std::string::npos) { + throw sycl::runtime_error("Malformed syntax in SYCL_DEVICE_ALLOWLIST", + PI_INVALID_VALUE); + } + std::string Value = AllowList.substr(Start, Pos - Start); + Pos = Pos + 2; + return Value; +} + +struct DevDescT { + std::string DevName; + std::string DevDriverVer; + std::string PlatName; + std::string PlatVer; +}; + +static std::vector getAllowListDesc() { + std::string AllowList(SYCLConfig::get()); + if (AllowList.empty()) + return {}; + + std::string DeviceName("DeviceName:"); + std::string DriverVersion("DriverVersion:"); + std::string PlatformName("PlatformName:"); + std::string PlatformVersion("PlatformVersion:"); + std::vector DecDescs; + DecDescs.emplace_back(); + + size_t Pos = 0; + while (Pos < AllowList.size()) { + if ((AllowList.compare(Pos, DeviceName.size(), DeviceName)) == 0) { + DecDescs.back().DevName = + getAllowListValue(AllowList, Pos, DeviceName.size()); + if (AllowList[Pos] == ',') { + Pos++; + } + } + + else if ((AllowList.compare(Pos, DriverVersion.size(), DriverVersion)) == + 0) { + DecDescs.back().DevDriverVer = + getAllowListValue(AllowList, Pos, DriverVersion.size()); + if (AllowList[Pos] == ',') { + Pos++; + } + } + + else if ((AllowList.compare(Pos, PlatformName.size(), PlatformName)) == 0) { + DecDescs.back().PlatName = + getAllowListValue(AllowList, Pos, PlatformName.size()); + if (AllowList[Pos] == ',') { + Pos++; + } + } + + else if ((AllowList.compare(Pos, PlatformVersion.size(), + PlatformVersion)) == 0) { + DecDescs.back().PlatVer = + getAllowListValue(AllowList, Pos, PlatformVersion.size()); + } else if (AllowList.find('|', Pos) != std::string::npos) { + Pos = AllowList.find('|') + 1; + while (AllowList[Pos] == ' ') { + Pos++; + } + DecDescs.emplace_back(); + } + + else { + throw sycl::runtime_error("Unrecognized key in device allowlist", + PI_INVALID_VALUE); + } + } // while (Pos <= AllowList.size()) + return DecDescs; +} + +enum class FilterState { DENIED, ALLOWED }; + +static void filterAllowList(vector_class &PiDevices, + RT::PiPlatform PiPlatform, const plugin &Plugin) { + const std::vector AllowList(getAllowListDesc()); + if (AllowList.empty()) + return; + + FilterState DevNameState = FilterState::ALLOWED; + FilterState DevVerState = FilterState::ALLOWED; + FilterState PlatNameState = FilterState::ALLOWED; + FilterState PlatVerState = FilterState::ALLOWED; + + const string_class PlatformName = + sycl::detail::get_platform_info::get( + PiPlatform, Plugin); + + const string_class PlatformVer = + sycl::detail::get_platform_info::get(PiPlatform, + Plugin); + + int InsertIDx = 0; + for (RT::PiDevice Device : PiDevices) { + const string_class DeviceName = + sycl::detail::get_device_info::get( + Device, Plugin); + + const string_class DeviceDriverVer = sycl::detail::get_device_info< + string_class, info::device::driver_version>::get(Device, Plugin); + + for (const DevDescT &Desc : AllowList) { + if (!Desc.PlatName.empty()) { + if (!std::regex_match(PlatformName, std::regex(Desc.PlatName))) { + PlatNameState = FilterState::DENIED; + continue; + } + } + + if (!Desc.PlatVer.empty()) { + if (!std::regex_match(PlatformVer, std::regex(Desc.PlatVer))) { + PlatVerState = FilterState::DENIED; + continue; + } + } + + if (!Desc.DevName.empty()) { + if (!std::regex_match(DeviceName, std::regex(Desc.DevName))) { + DevNameState = FilterState::DENIED; + continue; + } + } + + if (!Desc.DevDriverVer.empty()) { + if (!std::regex_match(DeviceDriverVer, std::regex(Desc.DevDriverVer))) { + DevVerState = FilterState::DENIED; + continue; + } + } + + if (DevNameState == FilterState::ALLOWED && + DevVerState == FilterState::ALLOWED && + PlatNameState == FilterState::ALLOWED && + PlatVerState == FilterState::ALLOWED) + PiDevices[InsertIDx++] = Device; + break; + } + } + PiDevices.resize(InsertIDx); +} + +// Filter out the devices that are not compatible with SYCL_DEVICE_FILTER. +// All three entries (backend:device_type:device_num) are optional. +// The missing entries are constructed using '*', which means 'any' | 'all' +// by the device_filter constructor. +// This function matches devices in the order of backend, device_type, and +// device_num. +static void filterDeviceFilter(vector_class &PiDevices, + RT::PiPlatform Platform, const plugin &Plugin, + int DeviceNum) { + device_filter_list *FilterList = SYCLConfig::get(); + if (!FilterList) + return; + + backend Backend = Plugin.getBackend(); + int InsertIDx = 0; + + for (RT::PiDevice Device : PiDevices) { + RT::PiDeviceType PiDevType; + Plugin.call(Device, PI_DEVICE_INFO_TYPE, + sizeof(RT::PiDeviceType), + &PiDevType, nullptr); + // Assumption here is that there is 1-to-1 mapping between PiDevType and + // Sycl device type for GPU, CPU, and ACC. + info::device_type DeviceType = pi::cast(PiDevType); + + for (const device_filter &Filter : FilterList->get()) { + backend FilterBackend = Filter.Backend; + // First, match the backend entry + if (FilterBackend == Backend || FilterBackend == backend::all) { + info::device_type FilterDevType = Filter.DeviceType; + // Next, match the device_type entry + if (FilterDevType == info::device_type::all) { + // Last, match the device_num entry + if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) { + PiDevices[InsertIDx++] = Device; + break; + } + } else if (FilterDevType == DeviceType) { + if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) { + PiDevices[InsertIDx++] = Device; + break; + } + } + } + } + DeviceNum++; + } + PiDevices.resize(InsertIDx); +} + +// Fill up the platform cache and device cache for the given Plugin. +// Thi( + 0, nullptr, &NumPlatforms) != PI_SUCCESS) + return; + + if (NumPlatforms) { + vector_class PiPlatforms(NumPlatforms); + if (Plugin.call_nocheck( + NumPlatforms, PiPlatforms.data(), nullptr) != PI_SUCCESS) + return; + + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); + + int DeviceNum = 0; + for (const auto &PiPlatform : PiPlatforms) { + PlatformImplPtr PlatformImpl = + std::make_shared(PiPlatform, Plugin); + platform Platform = detail::createSyclObjFromImpl(PlatformImpl); + + if (IsBannedPlatform(Platform)) + continue; + + // get devices + info::device_type DeviceType = info::device_type::all; + pi_uint32 NumDevices = 0; + Plugin.call( + PiPlatform, pi::cast(DeviceType), 0, + pi::cast(nullptr), &NumDevices); + + if (NumDevices == 0) + continue; + + vector_class PiDevices(NumDevices); + Plugin.call( + PiPlatform, pi::cast(DeviceType), NumDevices, + PiDevices.data(), nullptr); + + // Filter out devices that are not present in the allowlist + if (SYCLConfig::get()) + filterAllowList(PiDevices, PiPlatform, Plugin); + + int UnfilteredDeviceCount = PiDevices.size(); + // Filter out devices that are not compatible with SYCL_DEVICE_FILTER + filterDeviceFilter(PiDevices, PiPlatform, Plugin, DeviceNum); + + if (PiDevices.size() != 0) { + std::vector DeviceCache; + const std::lock_guard Guard( + GlobalHandler::instance().getPlatformMapMutex()); + for (const RT::PiDevice &PiDevice : PiDevices) { + std::shared_ptr Device = + std::make_shared(PiDevice, PlatformImpl); + DeviceCache.emplace_back(Device); + } + PlatformDeviceCache[PlatformImpl] = DeviceCache; + } + DeviceNum += UnfilteredDeviceCount; + } // end of for + } // end of if +} + // Initializes all available Plugins. const std::vector &initialize() { static std::once_flag PluginsInitDone; @@ -368,7 +674,6 @@ const std::vector &initialize() { std::call_once(PluginsInitDone, []() { initializePlugins(&GlobalHandler::instance().getPlugins()); }); - return GlobalHandler::instance().getPlugins(); } @@ -437,8 +742,25 @@ static void initializePlugins(std::vector *Plugins) { std::cerr << "SYCL_PI_TRACE[basic]: " << "Plugin found and successfully loaded: " << PluginNames[I].first << std::endl; - } + fillPlatformAndDeviceCache(Plugins->back()); + } // end of for + + // The host platform should always be available unless not allowed by the + // SYCL_DEVICE_FILTER + detail::device_filter_list *FilterList = + detail::SYCLConfig::get(); + if (!FilterList || FilterList->containsHost()) { + + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); + PlatformImplPtr PlatformImpl = platform_impl::getHostPlatformImpl(); + DeviceImplPtr Device = std::make_shared(); + std::vector DeviceCache{Device}; + const std::lock_guard Guard( + GlobalHandler::instance().getPlatformMapMutex()); + PlatformDeviceCache[PlatformImpl] = DeviceCache; + } #ifdef XPTI_ENABLE_INSTRUMENTATION if (!(xptiTraceEnabled() && !XPTIInitDone)) return; diff --git a/sycl/source/detail/platform_impl.cpp b/sycl/source/detail/platform_impl.cpp index 42a639dae7835..2ab263cd6ef85 100644 --- a/sycl/source/detail/platform_impl.cpp +++ b/sycl/source/detail/platform_impl.cpp @@ -28,7 +28,6 @@ using PlatformImplPtr = std::shared_ptr; PlatformImplPtr platform_impl::getHostPlatformImpl() { static PlatformImplPtr HostImpl = std::make_shared(); - return HostImpl; } @@ -39,18 +38,18 @@ PlatformImplPtr platform_impl::getOrMakePlatformImpl(RT::PiPlatform PiPlatform, const std::lock_guard Guard( GlobalHandler::instance().getPlatformMapMutex()); - std::vector &PlatformCache = - GlobalHandler::instance().getPlatformCache(); + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); // If we've already seen this platform, return the impl - for (const auto &PlatImpl : PlatformCache) { - if (PlatImpl->getHandleRef() == PiPlatform) - return PlatImpl; + for (const auto &Pair : PlatformDeviceCache) { + if (Pair.first->getHandleRef() == PiPlatform) + return Pair.first; } // Otherwise make the impl Result = std::make_shared(PiPlatform, Plugin); - PlatformCache.emplace_back(Result); + PlatformDeviceCache[Result] = std::vector(); } return Result; @@ -65,189 +64,66 @@ PlatformImplPtr platform_impl::getPlatformFromPiDevice(RT::PiDevice PiDevice, return getOrMakePlatformImpl(Plt, Plugin); } -static bool IsBannedPlatform(platform Platform) { - // The NVIDIA OpenCL platform is currently not compatible with DPC++ - // since it is only 1.2 but gets selected by default in many systems - // There is also no support on the PTX backend for OpenCL consumption, - // and there have been some internal reports. - // To avoid problems on default users and deployment of DPC++ on platforms - // where CUDA is available, the OpenCL support is disabled. - // - auto IsNVIDIAOpenCL = [](platform Platform) { - if (Platform.is_host()) - return false; - - const bool HasCUDA = Platform.get_info().find( - "NVIDIA CUDA") != std::string::npos; - const auto Backend = - detail::getSyclObjImpl(Platform)->getPlugin().getBackend(); - const bool IsCUDAOCL = (HasCUDA && Backend == backend::opencl); - if (detail::pi::trace(detail::pi::TraceLevel::PI_TRACE_ALL) && IsCUDAOCL) { - std::cout << "SYCL_PI_TRACE[all]: " - << "NVIDIA CUDA OpenCL platform found but is not compatible." - << std::endl; - } - return IsCUDAOCL; - }; - return IsNVIDIAOpenCL(Platform); -} - std::vector platform_impl::get_platforms() { std::vector Platforms; - const std::vector &Plugins = RT::initialize(); - - info::device_type ForcedType = detail::get_forced_type(); - for (unsigned int i = 0; i < Plugins.size(); i++) { - - pi_uint32 NumPlatforms = 0; - // Move to the next plugin if the plugin fails to initialize. - // This way platforms from other plugins get a chance to be discovered. - if (Plugins[i].call_nocheck( - 0, nullptr, &NumPlatforms) != PI_SUCCESS) - continue; - - if (NumPlatforms) { - std::vector PiPlatforms(NumPlatforms); - if (Plugins[i].call_nocheck( - NumPlatforms, PiPlatforms.data(), nullptr) != PI_SUCCESS) - return Platforms; - - for (const auto &PiPlatform : PiPlatforms) { - platform Platform = detail::createSyclObjFromImpl( - getOrMakePlatformImpl(PiPlatform, Plugins[i])); - // Skip platforms which do not contain requested device types - if (!Platform.get_devices(ForcedType).empty() && - !IsBannedPlatform(Platform)) - Platforms.push_back(Platform); - } - } + RT::initialize(); + const std::lock_guard Guard( + GlobalHandler::instance().getPlatformMapMutex()); + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); + for (const auto &Pair : PlatformDeviceCache) { + platform Platform = detail::createSyclObjFromImpl(Pair.first); + Platforms.push_back(Platform); } - - // The host platform should always be available unless not allowed by the - // SYCL_DEVICE_FILTER - detail::device_filter_list *FilterList = - detail::SYCLConfig::get(); - if (!FilterList || FilterList->backendCompatible(backend::host)) - Platforms.emplace_back(platform()); - return Platforms; } -// Filter out the devices that are not compatible with SYCL_DEVICE_FILTER. -// All three entries (backend:device_type:device_num) are optional. -// The missing entries are constructed using '*', which means 'any' | 'all' -// by the device_filter constructor. -// This function matches devices in the order of backend, device_type, and -// device_num. -static void filterDeviceFilter(std::vector &PiDevices, - const plugin &Plugin) { - device_filter_list *FilterList = SYCLConfig::get(); - if (!FilterList) - return; - - backend Backend = Plugin.getBackend(); - int InsertIDx = 0; - int DeviceNum = 0; - for (RT::PiDevice Device : PiDevices) { - RT::PiDeviceType PiDevType; - Plugin.call(Device, PI_DEVICE_INFO_TYPE, - sizeof(RT::PiDeviceType), - &PiDevType, nullptr); - // Assumption here is that there is 1-to-1 mapping between PiDevType and - // Sycl device type for GPU, CPU, and ACC. - info::device_type DeviceType = pi::cast(PiDevType); - - for (const device_filter &Filter : FilterList->get()) { - backend FilterBackend = Filter.Backend; - // First, match the backend entry - if (FilterBackend == Backend || FilterBackend == backend::all) { - info::device_type FilterDevType = Filter.DeviceType; - // Next, match the device_type entry - if (FilterDevType == info::device_type::all) { - // Last, match the device_num entry - if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) { - PiDevices[InsertIDx++] = Device; - break; - } - } else if (FilterDevType == DeviceType) { - if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) { - PiDevices[InsertIDx++] = Device; - break; - } - } - } - } - DeviceNum++; - } - PiDevices.resize(InsertIDx); -} - -std::shared_ptr platform_impl::getOrMakeDeviceImpl( +DeviceImplPtr platform_impl::getOrMakeDeviceImpl( RT::PiDevice PiDevice, const std::shared_ptr &PlatformImpl) { - const std::lock_guard Guard(MDeviceMapMutex); + + const std::lock_guard Guard( + GlobalHandler::instance().getPlatformMapMutex()); + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); // If we've already seen this device, return the impl - for (const std::weak_ptr &DeviceWP : MDeviceCache) { - if (std::shared_ptr Device = DeviceWP.lock()) { - if (Device->getHandleRef() == PiDevice) - return Device; - } + std::vector &DeviceCache = PlatformDeviceCache[PlatformImpl]; + for (const DeviceImplPtr &Device : DeviceCache) { + if (Device->getHandleRef() == PiDevice) + return Device; } // Otherwise make the impl std::shared_ptr Result = std::make_shared(PiDevice, PlatformImpl); - MDeviceCache.emplace_back(Result); - + DeviceCache.emplace_back(Result); return Result; } std::vector platform_impl::get_devices(info::device_type DeviceType) const { std::vector Res; - if (is_host() && (DeviceType == info::device_type::host || - DeviceType == info::device_type::all)) { - // If SYCL_DEVICE_FILTER is set, check if filter contains host. - device_filter_list *FilterList = SYCLConfig::get(); - if (!FilterList || FilterList->containsHost()) { - Res.push_back(device()); + const std::lock_guard Guard( + GlobalHandler::instance().getPlatformMapMutex()); + std::map> &PlatformDeviceCache = + GlobalHandler::instance().getPlatformDeviceCache(); + + // If we've already seen this device, return the impl + PlatformImplPtr Platform = nullptr; + for (const auto &Pair : PlatformDeviceCache) { + if (Pair.first.get() == this) { + Platform = Pair.first; } } - - // If any DeviceType other than host was requested for host platform, - // an empty vector will be returned. - if (is_host() || DeviceType == info::device_type::host) - return Res; - - pi_uint32 NumDevices = 0; - const detail::plugin &Plugin = getPlugin(); - Plugin.call( - MPlatform, pi::cast(DeviceType), 0, - pi::cast(nullptr), &NumDevices); - - if (NumDevices == 0) - return Res; - - std::vector PiDevices(NumDevices); - // TODO catch an exception and put it to list of asynchronous exceptions - Plugin.call(MPlatform, - pi::cast(DeviceType), - NumDevices, PiDevices.data(), nullptr); - - // Filter out devices that are not present in the SYCL_DEVICE_ALLOWLIST - if (SYCLConfig::get()) - applyAllowList(PiDevices, MPlatform, this->getPlugin()); - - // Filter out devices that are not compatible with SYCL_DEVICE_FILTER - filterDeviceFilter(PiDevices, Plugin); - - PlatformImplPtr PlatformImpl = getOrMakePlatformImpl(MPlatform, *MPlugin); - std::transform( - PiDevices.begin(), PiDevices.end(), std::back_inserter(Res), - [PlatformImpl](const RT::PiDevice &PiDevice) -> device { - return detail::createSyclObjFromImpl( - PlatformImpl->getOrMakeDeviceImpl(PiDevice, PlatformImpl)); - }); + std::vector &DeviceCache = PlatformDeviceCache[Platform]; + for (const DeviceImplPtr &Device : DeviceCache) { + // Assumption here is that there is 1-to-1 mapping between PiDevType and + // Sycl device type for GPU, CPU, and ACC. + info::device_type PiDeviceType = + pi::cast(Device->get_device_type()); + if (DeviceType == info::device_type::all || DeviceType == PiDeviceType) + Res.push_back(detail::createSyclObjFromImpl(Device)); + } return Res; } diff --git a/sycl/source/detail/platform_impl.hpp b/sycl/source/detail/platform_impl.hpp index 37c5f03aa0a64..c04eb290b04af 100644 --- a/sycl/source/detail/platform_impl.hpp +++ b/sycl/source/detail/platform_impl.hpp @@ -184,8 +184,6 @@ class platform_impl { bool MHostPlatform = false; RT::PiPlatform MPlatform = 0; std::shared_ptr MPlugin; - std::vector> MDeviceCache; - std::mutex MDeviceMapMutex; }; } // namespace detail diff --git a/sycl/source/device.cpp b/sycl/source/device.cpp index f2e09cd1d0e61..bf8c5b7d4f1f6 100644 --- a/sycl/source/device.cpp +++ b/sycl/source/device.cpp @@ -50,21 +50,6 @@ device::device(const device_selector &deviceSelector) { std::vector device::get_devices(info::device_type deviceType) { std::vector devices; - detail::device_filter_list *FilterList = - detail::SYCLConfig::get(); - // Host device availability should depend on the forced type - bool includeHost = false; - // If SYCL_DEVICE_FILTER is set, we don't automatically include it. - // We will check if host devices are specified in the filter below. - if (FilterList) { - if (deviceType != info::device_type::host && - deviceType != info::device_type::all) - includeHost = false; - else - includeHost = FilterList->containsHost(); - } else { - includeHost = detail::match_types(deviceType, info::device_type::host); - } info::device_type forced_type = detail::get_forced_type(); // Exclude devices which do not match requested device type if (detail::match_types(deviceType, forced_type)) { @@ -76,22 +61,10 @@ std::vector device::get_devices(info::device_type deviceType) { if (ForcedBackend) if (!plt.is_host() && plt.get_backend() != *ForcedBackend) continue; - // If SYCL_DEVICE_FILTER is set, skip platforms that is incompatible - // with the filter specification. - if (FilterList && !FilterList->backendCompatible(plt.get_backend())) - continue; - - if (includeHost && plt.is_host()) { - std::vector host_device( - plt.get_devices(info::device_type::host)); - if (!host_device.empty()) - devices.insert(devices.end(), host_device.begin(), host_device.end()); - } else { - std::vector found_devices(plt.get_devices(deviceType)); - if (!found_devices.empty()) - devices.insert(devices.end(), found_devices.begin(), - found_devices.end()); - } + std::vector found_devices(plt.get_devices(deviceType)); + if (!found_devices.empty()) + devices.insert(devices.end(), found_devices.begin(), + found_devices.end()); } } return devices; diff --git a/sycl/test/regression/sycl-ls-unique-device-id-level-zero.cpp b/sycl/test/regression/sycl-ls-unique-device-id-level-zero.cpp new file mode 100644 index 0000000000000..0b609156a919e --- /dev/null +++ b/sycl/test/regression/sycl-ls-unique-device-id-level-zero.cpp @@ -0,0 +1,15 @@ +// REQUIRES: level_zero + +// RUN: env SYCL_DEVICE_FILTER=level_zero sycl-ls | FileCheck %s --check-prefixes=CHECK-LEVELZERO + +// CHECK-LEVELZERO-COUNT-1: [level_zero:{{.*}}:0] +// CHECK-LEVELZERO-NOT: [level_zero:{{.*}}:0] + +//==-- sycl-ls-unique-device-id-level-zero.cpp - SYCL test for unique device id +//--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// \ No newline at end of file diff --git a/sycl/test/regression/sycl-ls-unique-device-id-opencl.cpp b/sycl/test/regression/sycl-ls-unique-device-id-opencl.cpp new file mode 100644 index 0000000000000..599187b3e3365 --- /dev/null +++ b/sycl/test/regression/sycl-ls-unique-device-id-opencl.cpp @@ -0,0 +1,15 @@ +// REQUIRES: opencl + +// RUN: env SYCL_DEVICE_FILTER=opencl sycl-ls | FileCheck %s --check-prefixes=CHECK-OPENCL + +// CHECK-OPENCL-COUNT-1: [opencl:{{.*}}:0] +// CHECK-OPENCL-NOT: [opencl:{{.*}}:0] + +//==-- sycl-ls-unique-device-id-opencl.cpp - SYCL test for unique device id +//--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// \ No newline at end of file diff --git a/sycl/tools/sycl-ls/CMakeLists.txt b/sycl/tools/sycl-ls/CMakeLists.txt index a409db2d79d5f..f5909cf49d1c5 100644 --- a/sycl/tools/sycl-ls/CMakeLists.txt +++ b/sycl/tools/sycl-ls/CMakeLists.txt @@ -1,9 +1,16 @@ add_executable(sycl-ls sycl-ls.cpp) add_dependencies(sycl-ls sycl) target_include_directories(sycl-ls PRIVATE "${sycl_inc_dir}") + +set(sycl_lib sycl) +string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type_lower) +if (WIN32 AND "${build_type_lower}" MATCHES "debug") + set(sycl_lib sycld) +endif() + target_link_libraries(sycl-ls PRIVATE - sycl + ${sycl_lib} OpenCL-Headers ) install(TARGETS sycl-ls diff --git a/sycl/tools/sycl-ls/sycl-ls.cpp b/sycl/tools/sycl-ls/sycl-ls.cpp index a50371ed8c934..b9b38ca73e91b 100644 --- a/sycl/tools/sycl-ls/sycl-ls.cpp +++ b/sycl/tools/sycl-ls/sycl-ls.cpp @@ -38,44 +38,38 @@ class custom_selector : public device_selector { } }; -static void printDeviceInfo(const device &Device, const std::string &Prepend) { +std::string getDeviceTypeName(const device &Device) { auto DeviceType = Device.get_info(); - std::string DeviceTypeName; switch (DeviceType) { case info::device_type::cpu: - DeviceTypeName = "CPU "; - break; + return "cpu"; case info::device_type::gpu: - DeviceTypeName = "GPU "; - break; + return "gpu"; case info::device_type::host: - DeviceTypeName = "HOST"; - break; + return "host"; case info::device_type::accelerator: - DeviceTypeName = "ACC "; - break; + return "acc"; default: - DeviceTypeName = "UNKNOWN"; - break; + return "unknown"; } +} +static void printDeviceInfo(const device &Device, const std::string &Prepend) { auto DeviceVersion = Device.get_info(); auto DeviceName = Device.get_info(); auto DeviceVendor = Device.get_info(); auto DeviceDriverVersion = Device.get_info(); if (verbose) { - std::cout << Prepend << "Type : " << DeviceTypeName << std::endl; + std::cout << Prepend << "Type : " << getDeviceTypeName(Device) + << std::endl; std::cout << Prepend << "Version : " << DeviceVersion << std::endl; std::cout << Prepend << "Name : " << DeviceName << std::endl; std::cout << Prepend << "Vendor : " << DeviceVendor << std::endl; std::cout << Prepend << "Driver : " << DeviceDriverVersion << std::endl; } else { - auto DevicePlatform = Device.get_info(); - auto DevicePlatformName = DevicePlatform.get_info(); - std::cout << Prepend << DeviceTypeName << ": " << DevicePlatformName << " " - << DeviceVersion << " [" << DeviceDriverVersion << "]" - << std::endl; + std::cout << Prepend << " : " << DeviceName << " " << DeviceVersion << " [" + << DeviceDriverVersion << "]" << std::endl; } } @@ -83,8 +77,8 @@ static void printSelectorChoice(const device_selector &Selector, const std::string &Prepend) { try { const auto &Dev = device(Selector); - printDeviceInfo(Dev, Prepend); - + std::string DeviceTypeName = getDeviceTypeName(Dev); + printDeviceInfo(Dev, Prepend + DeviceTypeName); } catch (const cl::sycl::runtime_error &Exception) { // Truncate long string so it can fit in one-line std::string What = Exception.what(); @@ -106,14 +100,26 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + const char *filter = std::getenv("SYCL_DEVICE_FILTER"); + if (filter) { + std::cout << "Warning: SYCL_DEVICE_FILTER environment variable is set to " + << filter << "." << std::endl; + std::cout + << "To see the correct device id, please unset SYCL_DEVICE_FILTER." + << std::endl + << std::endl; + } + const auto &Platforms = platform::get_platforms(); if (verbose) std::cout << "Platforms: " << Platforms.size() << std::endl; uint32_t PlatformNum = 0; + // For each backend, device num starts at zero. + std::vector DeviceNums(static_cast(backend::all), 0); for (const auto &Platform : Platforms) { - uint32_t DeviceNum = 0; + backend Backend = Platform.get_backend(); ++PlatformNum; if (verbose) { auto PlatformVersion = Platform.get_info(); @@ -128,11 +134,12 @@ int main(int argc, char **argv) { if (verbose) std::cout << " Devices : " << Devices.size() << std::endl; for (const auto &Device : Devices) { + uint32_t DeviceNum = DeviceNums[(int)Backend]++; if (verbose) std::cout << " Device [#" << DeviceNum << "]:" << std::endl; else { - backend Backend = Platform.get_backend(); - std::cout << "[" << Backend << ":" << DeviceNum << "] "; + std::cout << "[" << Backend << ":" << getDeviceTypeName(Device) << ":" + << DeviceNum << "]"; } ++DeviceNum; printDeviceInfo(Device, verbose ? " " : ""); diff --git a/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp b/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp index 151c56d8e9a8a..0b6d243f48339 100644 --- a/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp +++ b/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp @@ -102,7 +102,6 @@ TEST(GetNative, GetNativeHandle) { get_native(Device); get_native(Event); - // When creating a context, the piDeviceRetain is called so here is the 6 - // retain calls - ASSERT_EQ(TestCounter, 6) << "Not all the retain methods was called"; + ASSERT_EQ(TestCounter, 5) + << "Not all the retain methods was called " << TestCounter; } diff --git a/sycl/unittests/allowlist/ParseAllowList.cpp b/sycl/unittests/allowlist/ParseAllowList.cpp index 0791e48c586ac..5f400cd1c872a 100644 --- a/sycl/unittests/allowlist/ParseAllowList.cpp +++ b/sycl/unittests/allowlist/ParseAllowList.cpp @@ -165,9 +165,9 @@ TEST(ParseAllowListTests, CheckAllValidBackendNameValuesAreProcessed) { sycl::detail::AllowListParsedT ActualValue = sycl::detail::parseAllowList(AllowList); sycl::detail::AllowListParsedT ExpectedValue{ - {{"BackendName", "host"}}, {{"BackendName", "opencl"}}, - {{"BackendName", "level_zero"}}, {{"BackendName", "cuda"}}, - {{"BackendName", "rocm"}}, {{"BackendName", "*"}}}; + {{"BackendName", "opencl"}}, {{"BackendName", "level_zero"}}, + {{"BackendName", "cuda"}}, {{"BackendName", "rocm"}}, + {{"BackendName", "host"}}, {{"BackendName", "*"}}}; EXPECT_EQ(ExpectedValue, ActualValue); }