Skip to content

Commit 14e227c

Browse files
authored
[SYCL] Implement new env var SYCL_DEVICE_FILTER (#2239)
* [SYCL] Implement new env var SYCL_DEVICE_TRIPLE This new env var takes a list of triples {device_type, backend, device_num} 1. This list means SYCL_RT will only use those specified devices. 2. This list also limits related plugins to be loaded by SYCL RT. This PR only implemented new env var and selective plugin loading (#2) Signed-off-by: Byoungro So <byoungro.so@intel.com>
1 parent d31184e commit 14e227c

17 files changed

+766
-28
lines changed

sycl/doc/EnvironmentVariables.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +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. |
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. |
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::platform::get_device() call, 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, including "host" in the list of filters is allowed but is unnecessary. 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. This environment variable can be used to limit loading only specified plugins into the SYCL RT. |
1718
| SYCL_PROGRAM_COMPILE_OPTIONS | String of valid OpenCL compile options | Override compile options for all programs. |
1819
| SYCL_PROGRAM_LINK_OPTIONS | String of valid OpenCL link options | Override link options for all programs. |
1920
| 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.|

sycl/include/CL/sycl/backend_types.hpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,26 @@
1818
__SYCL_INLINE_NAMESPACE(cl) {
1919
namespace sycl {
2020

21-
enum class backend : char { host, opencl, level_zero, cuda };
21+
enum class backend : char { host, opencl, level_zero, cuda, all };
2222

2323
template <backend name, typename SYCLObjectT> struct interop;
2424

2525
inline std::ostream &operator<<(std::ostream &Out, backend be) {
2626
switch (be) {
2727
case backend::host:
28-
Out << std::string("host");
28+
Out << "host";
2929
break;
3030
case backend::opencl:
31-
Out << std::string("opencl");
31+
Out << "opencl";
3232
break;
3333
case backend::level_zero:
34-
Out << std::string("level_zero");
34+
Out << "level_zero";
3535
break;
3636
case backend::cuda:
37-
Out << std::string("cuda");
37+
Out << "cuda";
38+
break;
39+
case backend::all:
40+
Out << "all";
3841
}
3942
return Out;
4043
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//==---------- device_filter.hpp - SYCL device filter descriptor -----------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#pragma once
10+
11+
#include <CL/sycl/backend_types.hpp>
12+
#include <CL/sycl/detail/defines.hpp>
13+
#include <CL/sycl/info/info_desc.hpp>
14+
15+
#include <iostream>
16+
#include <string>
17+
18+
__SYCL_INLINE_NAMESPACE(cl) {
19+
namespace sycl {
20+
namespace detail {
21+
22+
struct device_filter {
23+
backend Backend = backend::all;
24+
info::device_type DeviceType = info::device_type::all;
25+
int DeviceNum = 0;
26+
bool HasBackend = false;
27+
bool HasDeviceType = false;
28+
bool HasDeviceNum = false;
29+
int MatchesSeen = 0;
30+
31+
device_filter(){};
32+
device_filter(const std::string &FilterString);
33+
friend std::ostream &operator<<(std::ostream &Out,
34+
const device_filter &Filter);
35+
};
36+
37+
class device_filter_list {
38+
std::vector<device_filter> FilterList;
39+
40+
public:
41+
device_filter_list() {}
42+
device_filter_list(const std::string &FilterString);
43+
device_filter_list(device_filter &Filter);
44+
void addFilter(device_filter &Filter);
45+
std::vector<device_filter> &get() { return FilterList; }
46+
friend std::ostream &operator<<(std::ostream &Out,
47+
const device_filter_list &List);
48+
};
49+
50+
inline std::ostream &operator<<(std::ostream &Out,
51+
const device_filter &Filter) {
52+
Out << Filter.Backend << ":";
53+
if (Filter.DeviceType == info::device_type::host) {
54+
Out << "host";
55+
} else if (Filter.DeviceType == info::device_type::cpu) {
56+
Out << "cpu";
57+
} else if (Filter.DeviceType == info::device_type::gpu) {
58+
Out << "gpu";
59+
} else if (Filter.DeviceType == info::device_type::accelerator) {
60+
Out << "accelerator";
61+
} else if (Filter.DeviceType == info::device_type::all) {
62+
Out << "*";
63+
} else {
64+
Out << "unknown";
65+
}
66+
if (Filter.HasDeviceNum) {
67+
Out << ":" << Filter.DeviceNum;
68+
}
69+
return Out;
70+
}
71+
72+
inline std::ostream &operator<<(std::ostream &Out,
73+
const device_filter_list &List) {
74+
for (const device_filter &Filter : List.FilterList) {
75+
Out << Filter;
76+
Out << ",";
77+
}
78+
return Out;
79+
}
80+
81+
} // namespace detail
82+
} // namespace sycl
83+
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ set(SYCL_SOURCES
108108
"detail/config.cpp"
109109
"detail/context_impl.cpp"
110110
"detail/device_binary_image.cpp"
111+
"detail/device_filter.cpp"
111112
"detail/device_impl.cpp"
112113
"detail/error_handling/enqueue_kernel.cpp"
113114
"detail/event_impl.cpp"

sycl/source/detail/config.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ CONFIG(SYCL_DEVICE_ALLOWLIST, 1024, __SYCL_DEVICE_ALLOWLIST)
1616
CONFIG(SYCL_BE, 16, __SYCL_BE)
1717
CONFIG(SYCL_PI_TRACE, 16, __SYCL_PI_TRACE)
1818
CONFIG(SYCL_DEVICELIB_NO_FALLBACK, 1, __SYCL_DEVICELIB_NO_FALLBACK)
19+
CONFIG(SYCL_DEVICE_FILTER, 1024, __SYCL_DEVICE_FILTER)

sycl/source/detail/config.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
#include <CL/sycl/backend_types.hpp>
1212
#include <CL/sycl/detail/defines.hpp>
13+
#include <CL/sycl/detail/device_filter.hpp>
1314
#include <CL/sycl/detail/pi.hpp>
15+
#include <CL/sycl/info/info_desc.hpp>
1416

1517
#include <algorithm>
1618
#include <array>
@@ -163,6 +165,35 @@ template <> class SYCLConfig<SYCL_PI_TRACE> {
163165
}
164166
};
165167

168+
template <> class SYCLConfig<SYCL_DEVICE_FILTER> {
169+
using BaseT = SYCLConfigBase<SYCL_DEVICE_FILTER>;
170+
171+
public:
172+
static device_filter_list *get() {
173+
static bool Initialized = false;
174+
static device_filter_list *FilterList = nullptr;
175+
176+
// Configuration parameters are processed only once, like reading a string
177+
// from environment and converting it into a typed object.
178+
if (Initialized) {
179+
return FilterList;
180+
}
181+
182+
const char *ValStr = BaseT::getRawValue();
183+
if (ValStr) {
184+
static device_filter_list DFL{ValStr};
185+
FilterList = &DFL;
186+
}
187+
// As mentioned above, configuration parameters are processed only once.
188+
// If multiple threads are checking this env var at the same time,
189+
// they will end up setting the configration to the same value.
190+
// If other threads check after one thread already set configration,
191+
// the threads will get the same value as the first thread.
192+
Initialized = true;
193+
return FilterList;
194+
}
195+
};
196+
166197
} // namespace detail
167198
} // namespace sycl
168199
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/detail/device_filter.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//==------------------- device_filter.cpp ----------------------------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <CL/sycl/detail/device_filter.hpp>
10+
#include <CL/sycl/info/info_desc.hpp>
11+
#include <detail/config.hpp>
12+
#include <detail/device_impl.hpp>
13+
14+
#include <cstring>
15+
16+
__SYCL_INLINE_NAMESPACE(cl) {
17+
namespace sycl {
18+
namespace detail {
19+
20+
device_filter::device_filter(const std::string &FilterString) {
21+
const std::array<std::pair<std::string, info::device_type>, 5>
22+
SyclDeviceTypeMap = {{{"host", info::device_type::host},
23+
{"cpu", info::device_type::cpu},
24+
{"gpu", info::device_type::gpu},
25+
{"acc", info::device_type::accelerator},
26+
{"*", info::device_type::all}}};
27+
const std::array<std::pair<std::string, backend>, 5> SyclBeMap = {
28+
{{"host", backend::host},
29+
{"opencl", backend::opencl},
30+
{"level_zero", backend::level_zero},
31+
{"cuda", backend::cuda},
32+
{"*", backend::all}}};
33+
34+
size_t Cursor = 0;
35+
size_t ColonPos = 0;
36+
auto findElement = [&](auto Element) {
37+
size_t Found = FilterString.find(Element.first, Cursor);
38+
if (Found == std::string::npos)
39+
return false;
40+
Cursor = Found;
41+
return true;
42+
};
43+
auto selectElement = [&](auto It, auto Map, auto EltIfNotFound) {
44+
if (It == Map.end())
45+
return EltIfNotFound;
46+
ColonPos = FilterString.find(":", Cursor);
47+
if (ColonPos != std::string::npos)
48+
Cursor = ColonPos + 1;
49+
else
50+
Cursor = Cursor + It->first.size();
51+
return It->second;
52+
};
53+
54+
// Handle the optional 1st field of the filter, backend
55+
// Check if the first entry matches with a known backend type
56+
auto It =
57+
std::find_if(std::begin(SyclBeMap), std::end(SyclBeMap), findElement);
58+
// If no match is found, set the backend type backend::all
59+
// which actually means 'any backend' will be a match.
60+
Backend = selectElement(It, SyclBeMap, backend::all);
61+
62+
// Handle the optional 2nd field of the filter - device type.
63+
// Check if the 2nd entry matches with any known device type.
64+
if (Cursor >= FilterString.size()) {
65+
DeviceType = info::device_type::all;
66+
} else {
67+
auto Iter = std::find_if(std::begin(SyclDeviceTypeMap),
68+
std::end(SyclDeviceTypeMap), findElement);
69+
// If no match is found, set device_type 'all',
70+
// which actually means 'any device_type' will be a match.
71+
DeviceType = selectElement(Iter, SyclDeviceTypeMap, info::device_type::all);
72+
}
73+
74+
// Handle the optional 3rd field of the filter, device number
75+
// Try to convert the remaining string to an integer.
76+
// If succeessful, the converted integer is the desired device num.
77+
if (Cursor < FilterString.size()) {
78+
try {
79+
DeviceNum = stoi(FilterString.substr(ColonPos + 1));
80+
HasDeviceNum = true;
81+
} catch (...) {
82+
std::string Message =
83+
std::string("Invalid device filter: ") + FilterString +
84+
"\nPossible backend values are {host,opencl,level_zero,cuda,*}.\n"
85+
"Possible device types are {host,cpu,gpu,acc,*}.\n"
86+
"Device number should be an non-negative integer.\n";
87+
throw cl::sycl::invalid_parameter_error(Message, PI_INVALID_VALUE);
88+
}
89+
}
90+
}
91+
92+
device_filter_list::device_filter_list(const std::string &FilterStr) {
93+
// First, change the string in all lowercase.
94+
// This means we allow the user to use both uppercase and lowercase strings.
95+
std::string FilterString = FilterStr;
96+
std::transform(FilterString.begin(), FilterString.end(), FilterString.begin(),
97+
::tolower);
98+
// SYCL_DEVICE_FILTER can set multiple filters separated by commas.
99+
// convert each filter triple string into an istance of device_filter class.
100+
size_t Pos = 0;
101+
while (Pos < FilterString.size()) {
102+
size_t CommaPos = FilterString.find(",", Pos);
103+
if (CommaPos == std::string::npos) {
104+
CommaPos = FilterString.size();
105+
}
106+
std::string SubString = FilterString.substr(Pos, CommaPos - Pos);
107+
FilterList.push_back(device_filter(SubString));
108+
Pos = CommaPos + 1;
109+
}
110+
}
111+
112+
device_filter_list::device_filter_list(device_filter &Filter) {
113+
FilterList.push_back(Filter);
114+
}
115+
116+
void device_filter_list::addFilter(device_filter &Filter) {
117+
FilterList.push_back(Filter);
118+
}
119+
120+
} // namespace detail
121+
} // namespace sycl
122+
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/detail/filter_selector_impl.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ filter create_filter(const std::string &Input) {
6262

6363
for (const std::string &Token : Tokens) {
6464
if (Token == "cpu" && !Result.HasDeviceType) {
65-
Result.DeviceType = PI_DEVICE_TYPE_CPU;
65+
Result.DeviceType = info::device_type::cpu;
6666
Result.HasDeviceType = true;
6767
} else if (Token == "gpu" && !Result.HasDeviceType) {
68-
Result.DeviceType = PI_DEVICE_TYPE_GPU;
68+
Result.DeviceType = info::device_type::gpu;
6969
Result.HasDeviceType = true;
7070
} else if (Token == "accelerator" && !Result.HasDeviceType) {
71-
Result.DeviceType = PI_DEVICE_TYPE_ACC;
71+
Result.DeviceType = info::device_type::accelerator;
7272
Result.HasDeviceType = true;
7373
} else if (Token == "opencl" && !Result.HasBackend) {
7474
Result.Backend = backend::opencl;
@@ -134,8 +134,7 @@ int filter_selector_impl::operator()(const device &Dev) const {
134134
BackendOK = (BE == Filter.Backend);
135135
}
136136
if (Filter.HasDeviceType) {
137-
RT::PiDeviceType DT =
138-
sycl::detail::getSyclObjImpl(Dev)->get_device_type();
137+
info::device_type DT = Dev.get_info<info::device::device_type>();
139138
DeviceTypeOK = (DT == Filter.DeviceType);
140139
}
141140
if (Filter.HasDeviceNum) {

sycl/source/detail/filter_selector_impl.hpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#pragma once
1010

11+
#include <CL/sycl/detail/device_filter.hpp>
1112
#include <CL/sycl/device_selector.hpp>
1213

1314
#include <vector>
@@ -21,15 +22,7 @@ class device;
2122
namespace ONEAPI {
2223
namespace detail {
2324

24-
struct filter {
25-
backend Backend = backend::host;
26-
RT::PiDeviceType DeviceType = PI_DEVICE_TYPE_ALL;
27-
int DeviceNum = 0;
28-
bool HasBackend = false;
29-
bool HasDeviceType = false;
30-
bool HasDeviceNum = false;
31-
int MatchesSeen = 0;
32-
};
25+
typedef struct sycl::detail::device_filter filter;
3326

3427
class filter_selector_impl {
3528
public:

0 commit comments

Comments
 (0)