Skip to content

Commit ef4e6dd

Browse files
authored
[SYCL] Fix SYCL_DEVICE_FILTER to affect get_devices()/get_platforms() (#3397)
SYCL_DEVICE_FILTER will now throw an exception if no matched device is found regardless of the device_selector type. Host device is not automatically available if SYCL_DEVICE_FILTER is set. Tests configurations and documentation updated. Signed-off-by: Byoungro So <[email protected]>
1 parent 4fb95fc commit ef4e6dd

File tree

10 files changed

+189
-35
lines changed

10 files changed

+189
-35
lines changed

sycl/doc/EnvironmentVariables.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ subject to change. Do not rely on these variables in production code.
1212
| Environment variable | Values | Description |
1313
| -------------------- | ------ | ----------- |
1414
| SYCL_PI_TRACE | Described [below](#sycl_pi_trace-options) | Enable specified level of tracing for PI. |
15-
| SYCL_BE | PI_OPENCL, PI_LEVEL_ZERO, PI_CUDA | Force SYCL RT to consider only devices of the specified backend during the device selection. We are planning to deprecate SYCL_BE environment variable in the future. The specific grace period is not decided yet. Please use the new env var SYCL_DEVICE_FILTER instead. |
16-
| SYCL_DEVICE_TYPE | One of: CPU, GPU, ACC, HOST | Force SYCL to use the specified device type. If unset, default selection rules are applied. If set to any unlisted value, this control has no effect. If the requested device type is not found, a `cl::sycl::runtime_error` exception is thrown. If a non-default device selector is used, a device must satisfy both the selector and this control to be chosen. This control only has effect on devices created with a selector. We are planning to deprecate SYCL_DEVICE_TYPE environment variable in the future. The specific grace period is not decided yet. Please use the new env var SYCL_DEVICE_FILTER instead. |
17-
| SYCL_DEVICE_FILTER (tentative name) | {backend:device_type:device_num} | 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 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", "level_zero", "opencl", "cuda", or "\*". Possible values of "device_type" are "host", "cpu", "gpu", "acc", or "\*". 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. 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 always includes the "host" backend and the host device regardless of the filter because the SYCL language requires this device to always be present. Therefore, one can specify 'host' to enforce SYCL to use the host device. Note that the standard selectors like gpu_selector or cpu_selector will throw an exception if the filtered list of devices does not include a device that satisfies the selector. In particular, limiting the devices to only those supported by the "level_zero" backend will cause the cpu_selector to throw an exception since that backend does not support any CPU devices at this time. This environment variable can be used to limit loading only specified plugins into the SYCL RT. |
15+
| SYCL_BE (deprecated) | PI_OPENCL, PI_LEVEL_ZERO, PI_CUDA | Force SYCL RT to consider only devices of the specified backend during the device selection. We are planning to deprecate SYCL_BE environment variable in the future. The specific grace period is not decided yet. Please use the new env var SYCL_DEVICE_FILTER instead. |
16+
| SYCL_DEVICE_TYPE (deprecated) | CPU, GPU, ACC, HOST | Force SYCL to use the specified device type. If unset, default selection rules are applied. If set to any unlisted value, this control has no effect. If the requested device type is not found, a `cl::sycl::runtime_error` exception is thrown. If a non-default device selector is used, a device must satisfy both the selector and this control to be chosen. This control only has effect on devices created with a selector. We are planning to deprecate SYCL_DEVICE_TYPE environment variable in the future. The specific grace period is not decided yet. Please use the new env var SYCL_DEVICE_FILTER instead. |
17+
| SYCL_DEVICE_FILTER | backend:device_type:device_num | See Section [SYCL_DEVICE_FILTER](#sycl_device_filter) below. |
1818
| SYCL_PROGRAM_COMPILE_OPTIONS | String of valid OpenCL compile options | Override compile options for all programs. |
1919
| SYCL_PROGRAM_LINK_OPTIONS | String of valid OpenCL link options | Override link options for all programs. |
2020
| SYCL_USE_KERNEL_SPV | Path to the SPIR-V binary | Load device image from the specified file. If runtime is unable to read the file, `cl::sycl::runtime_error` exception is thrown.|
@@ -46,6 +46,30 @@ subject to change. Do not rely on these variables in production code.
4646

4747
`(*) Note: Any means this environment variable is effective when set to any non-null value.`
4848

49+
### SYCL_DEVICE_FILTER
50+
51+
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.
52+
53+
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:
54+
- host
55+
- level_zero
56+
- opencl
57+
- cuda
58+
- \*
59+
60+
Possible values of "device_type" are:
61+
- host
62+
- cpu
63+
- gpu
64+
- acc
65+
- \*
66+
67+
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.
68+
69+
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.
70+
71+
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.
72+
4973
### SYCL_PRINT_EXECUTION_GRAPH Options
5074

5175
SYCL_PRINT_EXECUTION_GRAPH can accept one or more comma separated values from the table below

sycl/include/CL/sycl/detail/device_filter.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class device_filter_list {
4343
device_filter_list(device_filter &Filter);
4444
void addFilter(device_filter &Filter);
4545
std::vector<device_filter> &get() { return FilterList; }
46+
bool backendCompatible(backend Backend);
47+
bool deviceTypeCompatible(info::device_type DeviceType);
48+
bool deviceNumberCompatible(int DeviceNum);
49+
bool containsHost();
4650
friend std::ostream &operator<<(std::ostream &Out,
4751
const device_filter_list &List);
4852
};

sycl/source/detail/device_filter.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,49 @@ void device_filter_list::addFilter(device_filter &Filter) {
124124
FilterList.push_back(Filter);
125125
}
126126

127+
// Backend is compatible with the SYCL_DEVICE_FILTER in the following cases.
128+
// 1. Filter backend is '*' which means ANY backend.
129+
// 2. Filter backend match exactly with the given 'Backend'
130+
bool device_filter_list::backendCompatible(backend Backend) {
131+
for (const device_filter &Filter : FilterList) {
132+
backend FilterBackend = Filter.Backend;
133+
if (FilterBackend == Backend || FilterBackend == backend::all)
134+
return true;
135+
}
136+
return false;
137+
}
138+
139+
bool device_filter_list::deviceTypeCompatible(info::device_type DeviceType) {
140+
for (const device_filter &Filter : FilterList) {
141+
info::device_type FilterDevType = Filter.DeviceType;
142+
if (FilterDevType == DeviceType || FilterDevType == info::device_type::all)
143+
return true;
144+
}
145+
return false;
146+
}
147+
148+
bool device_filter_list::deviceNumberCompatible(int DeviceNum) {
149+
for (const device_filter &Filter : FilterList) {
150+
int FilterDevNum = Filter.DeviceNum;
151+
if (!Filter.HasDeviceNum || FilterDevNum == DeviceNum)
152+
return true;
153+
}
154+
return false;
155+
}
156+
157+
bool device_filter_list::containsHost() {
158+
for (const device_filter &Filter : FilterList) {
159+
if (Filter.Backend == backend::host || Filter.Backend == backend::all)
160+
if (Filter.DeviceType == info::device_type::host ||
161+
Filter.DeviceType == info::device_type::all)
162+
// SYCL RT never creates more than one HOST device.
163+
// All device numbers other than 0 are rejected.
164+
if (!Filter.HasDeviceNum || Filter.DeviceNum == 0)
165+
return true;
166+
}
167+
return false;
168+
}
169+
127170
} // namespace detail
128171
} // namespace sycl
129172
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/detail/platform_impl.cpp

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@ vector_class<platform> platform_impl::get_platforms() {
123123
}
124124
}
125125

126-
// The host platform should always be available.
127-
Platforms.emplace_back(platform());
126+
// The host platform should always be available unless not allowed by the
127+
// SYCL_DEVICE_FILTER
128+
detail::device_filter_list *FilterList =
129+
detail::SYCLConfig<detail::SYCL_DEVICE_FILTER>::get();
130+
if (!FilterList || FilterList->backendCompatible(backend::host))
131+
Platforms.emplace_back(platform());
128132

129133
return Platforms;
130134
}
@@ -287,6 +291,55 @@ static void filterAllowList(vector_class<RT::PiDevice> &PiDevices,
287291
PiDevices.resize(InsertIDx);
288292
}
289293

294+
// Filter out the devices that are not compatible with SYCL_DEVICE_FILTER.
295+
// All three entries (backend:device_type:device_num) are optional.
296+
// The missing entries are constructed using '*', which means 'any' | 'all'
297+
// by the device_filter constructor.
298+
// This function matches devices in the order of backend, device_type, and
299+
// device_num.
300+
static void filterDeviceFilter(vector_class<RT::PiDevice> &PiDevices,
301+
const plugin &Plugin) {
302+
device_filter_list *FilterList = SYCLConfig<SYCL_DEVICE_FILTER>::get();
303+
if (!FilterList)
304+
return;
305+
306+
backend Backend = Plugin.getBackend();
307+
int InsertIDx = 0;
308+
int DeviceNum = 0;
309+
for (RT::PiDevice Device : PiDevices) {
310+
RT::PiDeviceType PiDevType;
311+
Plugin.call<PiApiKind::piDeviceGetInfo>(Device, PI_DEVICE_INFO_TYPE,
312+
sizeof(RT::PiDeviceType),
313+
&PiDevType, nullptr);
314+
// Assumption here is that there is 1-to-1 mapping between PiDevType and
315+
// Sycl device type for GPU, CPU, and ACC.
316+
info::device_type DeviceType = pi::cast<info::device_type>(PiDevType);
317+
318+
for (const device_filter &Filter : FilterList->get()) {
319+
backend FilterBackend = Filter.Backend;
320+
// First, match the backend entry
321+
if (FilterBackend == Backend || FilterBackend == backend::all) {
322+
info::device_type FilterDevType = Filter.DeviceType;
323+
// Next, match the device_type entry
324+
if (FilterDevType == info::device_type::all) {
325+
// Last, match the device_num entry
326+
if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) {
327+
PiDevices[InsertIDx++] = Device;
328+
break;
329+
}
330+
} else if (FilterDevType == DeviceType) {
331+
if (!Filter.HasDeviceNum || DeviceNum == Filter.DeviceNum) {
332+
PiDevices[InsertIDx++] = Device;
333+
break;
334+
}
335+
}
336+
}
337+
}
338+
DeviceNum++;
339+
}
340+
PiDevices.resize(InsertIDx);
341+
}
342+
290343
std::shared_ptr<device_impl> platform_impl::getOrMakeDeviceImpl(
291344
RT::PiDevice PiDevice, const std::shared_ptr<platform_impl> &PlatformImpl) {
292345
const std::lock_guard<std::mutex> Guard(MDeviceMapMutex);
@@ -312,7 +365,11 @@ platform_impl::get_devices(info::device_type DeviceType) const {
312365
vector_class<device> Res;
313366
if (is_host() && (DeviceType == info::device_type::host ||
314367
DeviceType == info::device_type::all)) {
315-
Res.push_back(device());
368+
// If SYCL_DEVICE_FILTER is set, check if filter contains host.
369+
device_filter_list *FilterList = SYCLConfig<SYCL_DEVICE_FILTER>::get();
370+
if (!FilterList || FilterList->containsHost()) {
371+
Res.push_back(device());
372+
}
316373
}
317374

318375
// If any DeviceType other than host was requested for host platform,
@@ -339,6 +396,9 @@ platform_impl::get_devices(info::device_type DeviceType) const {
339396
if (SYCLConfig<SYCL_DEVICE_ALLOWLIST>::get())
340397
filterAllowList(PiDevices, MPlatform, this->getPlugin());
341398

399+
// Filter out devices that are not compatible with SYCL_DEVICE_FILTER
400+
filterDeviceFilter(PiDevices, Plugin);
401+
342402
PlatformImplPtr PlatformImpl = getOrMakePlatformImpl(MPlatform, *MPlugin);
343403
std::transform(
344404
PiDevices.begin(), PiDevices.end(), std::back_inserter(Res),

sycl/source/device.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include <CL/sycl/detail/device_filter.hpp>
910
#include <CL/sycl/detail/export.hpp>
1011
#include <CL/sycl/device.hpp>
1112
#include <CL/sycl/device_selector.hpp>
@@ -49,9 +50,21 @@ device::device(const device_selector &deviceSelector) {
4950

5051
vector_class<device> device::get_devices(info::device_type deviceType) {
5152
vector_class<device> devices;
52-
// Host device availability should not depend on the forced type
53-
const bool includeHost =
54-
detail::match_types(deviceType, info::device_type::host);
53+
detail::device_filter_list *FilterList =
54+
detail::SYCLConfig<detail::SYCL_DEVICE_FILTER>::get();
55+
// Host device availability should depend on the forced type
56+
bool includeHost = false;
57+
// If SYCL_DEVICE_FILTER is set, we don't automatically include it.
58+
// We will check if host devices are specified in the filter below.
59+
if (FilterList) {
60+
if (deviceType != info::device_type::host &&
61+
deviceType != info::device_type::all)
62+
includeHost = false;
63+
else
64+
includeHost = FilterList->containsHost();
65+
} else {
66+
includeHost = detail::match_types(deviceType, info::device_type::host);
67+
}
5568
info::device_type forced_type = detail::get_forced_type();
5669
// Exclude devices which do not match requested device type
5770
if (detail::match_types(deviceType, forced_type)) {
@@ -61,10 +74,13 @@ vector_class<device> device::get_devices(info::device_type deviceType) {
6174
// backend.
6275
backend *ForcedBackend = detail::SYCLConfig<detail::SYCL_BE>::get();
6376
if (ForcedBackend)
64-
if (!plt.is_host() &&
65-
(detail::getSyclObjImpl(plt)->getPlugin().getBackend() !=
66-
*ForcedBackend))
77+
if (!plt.is_host() && plt.get_backend() != *ForcedBackend)
6778
continue;
79+
// If SYCL_DEVICE_FILTER is set, skip platforms that is incompatible
80+
// with the filter specification.
81+
if (FilterList && !FilterList->backendCompatible(plt.get_backend()))
82+
continue;
83+
6884
if (includeHost && plt.is_host()) {
6985
vector_class<device> host_device(
7086
plt.get_devices(info::device_type::host));
@@ -78,7 +94,6 @@ vector_class<device> device::get_devices(info::device_type deviceType) {
7894
}
7995
}
8096
}
81-
8297
return devices;
8398
}
8499

sycl/source/device_selector.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,18 @@ int default_selector::operator()(const device &dev) const {
137137
if (isDeviceOfPreferredSyclBe(dev))
138138
Score = 50;
139139

140-
// override always wins
141-
// filter device gets a high point.
142-
if (isForcedDevice(dev))
143-
Score += 1000;
140+
// If SYCL_DEVICE_FILTER is set, filter device gets a high point.
141+
// All unmatched devices should never be selected.
142+
detail::device_filter_list *FilterList =
143+
detail::SYCLConfig<detail::SYCL_DEVICE_FILTER>::get();
144+
if (FilterList) {
145+
if (isForcedDevice(dev))
146+
Score = 1000;
147+
else
148+
return REJECT_DEVICE_SCORE;
149+
}
144150

145-
else if (dev.get_info<info::device::device_type>() ==
146-
detail::get_forced_type())
151+
if (dev.get_info<info::device::device_type>() == detail::get_forced_type())
147152
Score += 1000;
148153

149154
if (dev.is_gpu())

sycl/test/Unit/lit.cfg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,5 @@ def find_shlibpath_var():
7373
lit_config.warning("unable to inject shared library path on '{}'"
7474
.format(platform.system()))
7575

76-
config.environment['SYCL_DEVICE_FILTER'] = lit_config.params.get('SYCL_PLUGIN', "opencl")
76+
config.environment['SYCL_DEVICE_FILTER'] = lit_config.params.get('SYCL_PLUGIN', "opencl") + ",host"
7777
lit_config.note("Backend: {}".format(config.environment['SYCL_DEVICE_FILTER']))

sycl/test/on-device/lit.cfg.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ def getDeviceCount(device_type):
188188
if getDeviceCount("cpu")[0]:
189189
found_at_least_one_device = True
190190
lit_config.note("Found available CPU device")
191-
cpu_run_substitute = "env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:cpu ".format(SYCL_PLUGIN=backend)
191+
cpu_run_substitute = "env SYCL_DEVICE_FILTER=cpu,host "
192192
cpu_check_substitute = "| FileCheck %s"
193193
config.available_features.add('cpu')
194194
if platform.system() == "Linux":
195-
cpu_run_on_linux_substitute = "env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:cpu ".format(SYCL_PLUGIN=backend)
195+
cpu_run_on_linux_substitute = "env SYCL_DEVICE_FILTER=cpu,host "
196196
cpu_check_on_linux_substitute = "| FileCheck %s"
197197
else:
198198
lit_config.warning("CPU device not found")
@@ -214,7 +214,7 @@ def getDeviceCount(device_type):
214214
if gpu_count > 0:
215215
found_at_least_one_device = True
216216
lit_config.note("Found available GPU device")
217-
gpu_run_substitute = " env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:gpu ".format(SYCL_PLUGIN=backend)
217+
gpu_run_substitute = " env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:gpu,host ".format(SYCL_PLUGIN=backend)
218218
gpu_check_substitute = "| FileCheck %s"
219219
config.available_features.add('gpu')
220220
if cuda:
@@ -223,7 +223,7 @@ def getDeviceCount(device_type):
223223
config.available_features.add('level_zero')
224224

225225
if platform.system() == "Linux":
226-
gpu_run_on_linux_substitute = "env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:gpu ".format(SYCL_PLUGIN=backend)
226+
gpu_run_on_linux_substitute = "env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:gpu,host ".format(SYCL_PLUGIN=backend)
227227
gpu_check_on_linux_substitute = "| FileCheck %s"
228228
else:
229229
lit_config.warning("GPU device not found")
@@ -238,7 +238,7 @@ def getDeviceCount(device_type):
238238
if getDeviceCount("accelerator")[0]:
239239
found_at_least_one_device = True
240240
lit_config.note("Found available accelerator device")
241-
acc_run_substitute = " env SYCL_DEVICE_FILTER={SYCL_PLUGIN}:acc ".format(SYCL_PLUGIN=backend)
241+
acc_run_substitute = " env SYCL_DEVICE_FILTER=acc "
242242
acc_check_substitute = "| FileCheck %s"
243243
config.available_features.add('accelerator')
244244
else:

sycl/tools/sycl-ls/sycl-ls.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,9 @@ int main(int argc, char **argv) {
111111
std::cout << "Platforms: " << Platforms.size() << std::endl;
112112

113113
uint32_t PlatformNum = 0;
114-
// DeviceNum represents a globally unique device number.
115-
// It is printed at the beginning of each line from 'sycl-ls'.
116-
// This number starts at 0.
117-
uint32_t DeviceNum = 0;
114+
118115
for (const auto &Platform : Platforms) {
116+
uint32_t DeviceNum = 0;
119117
++PlatformNum;
120118
if (verbose) {
121119
auto PlatformVersion = Platform.get_info<info::platform::version>();
@@ -132,8 +130,10 @@ int main(int argc, char **argv) {
132130
for (const auto &Device : Devices) {
133131
if (verbose)
134132
std::cout << " Device [#" << DeviceNum << "]:" << std::endl;
135-
else
136-
std::cout << DeviceNum << ". ";
133+
else {
134+
backend Backend = Platform.get_backend();
135+
std::cout << "[" << Backend << ":" << DeviceNum << "] ";
136+
}
137137
++DeviceNum;
138138
printDeviceInfo(Device, verbose ? " " : "");
139139
}

0 commit comments

Comments
 (0)