Skip to content

[SYCL] Add ONEAPI_DEVICE_SELECTOR implementation #6779

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a03226d
initial commit
cperkinsintel Sep 10, 2022
98ee8ac
hook environment var up to SYCL.Config. Working.
cperkinsintel Sep 12, 2022
a004eff
too fancy. checkpointing.
cperkinsintel Sep 13, 2022
5bd21e0
done, modulo tests, testing and testacularization
cperkinsintel Sep 14, 2022
94b076d
update message in sycl-ls
cperkinsintel Sep 14, 2022
55aaad8
reviewer feedback, bug-fixes
cperkinsintel Sep 14, 2022
5dc0a12
updated environment variable docs. prose is perfect as written. perfe…
cperkinsintel Sep 14, 2022
125fb95
minor updates per feedback, changes
cperkinsintel Sep 15, 2022
e161400
remove iostream, add fpga, and introduce std::optional. Probably shou…
cperkinsintel Sep 16, 2022
de06630
errors for parsing only, not device matching
cperkinsintel Sep 16, 2022
c99d8ea
clean up some comments
cperkinsintel Sep 16, 2022
82cea81
potential errors output as messages now. and review feedback, and a s…
cperkinsintel Sep 16, 2022
7ed9934
minor fix for unit tests
cperkinsintel Sep 19, 2022
e9047dc
warning when no matching devices
cperkinsintel Sep 19, 2022
62aa821
incorrect warning
cperkinsintel Sep 20, 2022
68ffa39
sub-sub device support added. device counting discrepancy with sycl-l…
cperkinsintel Sep 21, 2022
4c0ec48
tidy
cperkinsintel Sep 21, 2022
f685ce2
resolved merge conflicts
cperkinsintel Sep 21, 2022
b3b8a77
reviewer feedback
cperkinsintel Sep 21, 2022
9b8e468
fixed windows build issue, updated docs with sub-sub info and example
cperkinsintel Sep 22, 2022
533677d
reviewer feedback to .md file
cperkinsintel Sep 23, 2022
1e91fda
reviewer feedback
cperkinsintel Sep 28, 2022
41d6d3d
more restrictive parsing
cperkinsintel Sep 28, 2022
c59a7d9
requested documentation changes
cperkinsintel Oct 5, 2022
da634ea
last doc feedback incorporated
cperkinsintel Oct 5, 2022
db85931
spaces added to Level Zero
cperkinsintel Oct 5, 2022
cc35153
Merge branch 'sycl' into cperkins-oneapi_device_selector
cperkinsintel Oct 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions sycl/doc/EnvironmentVariables.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ compiler and runtime.

| Environment variable | Values | Description |
| -------------------- | ------ | ----------- |
| `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. |
| `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 `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. |
| `ONEAPI_DEVICE_SELECTOR` | [See below.](#oneapi_device_selector) | This device selection environment variable can be used to limit the choice of devices available when the SYCL-using application is run. Useful for limiting devices to a certain type (like GPUs or accelerators) or backends (like Level Zero or OpenCL). This device selection mechanism is replacing `SYCL_DEVICE_FILTER` . The `ONEAPI_DEVICE_SELECTOR` syntax is shared with OpenMP and also allows sub-devices to be chosen. [See below.](#oneapi_device_selector) for a full description. |
| `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. The `SYCL_BE` environment variable is deprecated and will be removed soon. Please use the new env var `ONEAPI_DEVICE_SELECTOR` instead. |
| `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 `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. The `SYCL_DEVICE_TYPE` environment variable is deprecated and will be removed soon. Please use the new env var `ONEAPI_DEVICE_SELECTOR` instead. |
| `SYCL_DEVICE_FILTER` | `backend:device_type:device_num` | See Section [`SYCL_DEVICE_FILTER`](#sycl_device_filter) below. |
| `SYCL_DEVICE_ALLOWLIST` | See [below](#sycl_device_allowlist) | Filter out devices that do not match the pattern specified. `BackendName` accepts `host`, `opencl`, `level_zero` or `cuda`. `DeviceType` accepts `host`, `cpu`, `gpu` or `acc`. `DeviceVendorId` accepts uint32_t in hex form (`0xXYZW`). `DriverVersion`, `PlatformVersion`, `DeviceName` and `PlatformName` accept regular expression. Special characters, such as parenthesis, must be escaped. DPC++ runtime will select only those devices which satisfy provided values above and regex. More than one device can be specified using the piping symbol "\|".|
| `SYCL_DISABLE_PARALLEL_FOR_RANGE_ROUNDING` | Any(\*) | Disables automatic rounding-up of `parallel_for` invocation ranges. |
Expand All @@ -28,6 +29,55 @@ compiler and runtime.

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

### `ONEAPI_DEVICE_SELECTOR`

With no environment variables set to say otherwise, all platforms and devices presently on the machine are available. The default choice will be one of these devices, usually preferring a Level Zero GPU device, if available. The `ONEAPI_DEVICE_SELECTOR` can be used to limit that choice of devices, and to expose GPU sub-devices or sub-sub-devices as individual devices.

The syntax of this environment variable follows this BNF grammar:
```
ONEAPI_DEVICE_SELECTOR = <selector-string>
<selector-string> ::= <term>[;<term>...]
<term> ::= <backend>:<devices>
<backend> ::= { * | level_zero | opencl | cuda | hip | esimd_emulator } // case insensitive
<devices> ::= <device>[,<device>...]
<device> ::= { * | cpu | gpu | fpga | <num> | <num>.<num> | <num>.* | *.* | <num>.<num>.<num> | <num>.<num>.* | <num>.*.* | *.*.* } // case insensitive
```

Each term in the grammar selects a collection of devices from a particular backend. The device names cpu, gpu, and fpga select all devices from that backend with the corresponding type. A backend's device can also be selected by its numeric index (zero-based) or by using `*` which selects all devices in the backend.

The dot syntax (e.g. `<num>.<num>`) causes one or more GPU sub-devices to be exposed to the application as SYCL root devices. For example, `1.0` exposes the first sub-device of the second device as a SYCL root device. The syntax `<num>.*` exposes all sub-devices of the give device as SYCL root devices. The syntax `*.*` exposes all sub-devices of all GPU devices as SYCL root devices.

In general, a term with one or more asterisks ( `*` ) matches all backends, devices, or sub-devices with the given pattern. However, a warning is generated if the term does not match anything. For example, `*:gpu` matches all GPU devices in all backends (ignoring backends with no GPU devices), but it generates a warning if there are no GPU devices in any backend. Likewise, `level_zero:*.*` matches all sub-devices of partitionable GPUs in the Level Zero backend, but it generates a warning if there are no Level Zero GPU devices that are partitionable into sub-devices.

The device indices are zero-based and are unique only within a backend. Therefore, `level_zero:0` is a different device from `cuda:0`. To see the indices of all available devices, run the `sycl-ls` tool. Note that different backends sometimes expose the same hardware as different "devices". For example, the level_zero and opencl backends both expose the Intel GPU devices.


Additionally, if a sub-device is chosen (via numeric index or wildcard), then an additional layer of partitioning can be specified. In other words, a sub-sub-device can be selected. Like sub-devices, this is done with a period ( `.` ) and a sub-sub-device specifier which is a wildcard symbol ( `*` ) or a numeric index. Example `ONEAPI_DEVICE_SELECTOR=level_zero:0.*.*` would partition device 0 into sub-devices and then partition each of those into sub-sub-devices. The range of grandchild sub-sub-devices would be the final devices available to the app, neither device 0, nor its child partitions would be in that list.


The following examples further illustrate the usage of this environment variable:

| Example | Result |
-----------|---------
| `ONEAPI_DEVICE_SELECTOR=opencl:*` | Only the OpenCL devices are available |
| `ONEAPI_DEVICE_SELECTOR=level_zero:gpu` | Only GPU devices on the Level Zero platform are available.|
| `ONEAPI_DEVICE_SELECTOR="opencl:gpu;level_zero:gpu"` | GPU devices from both Level Zero and OpenCL are available. Note that escaping (like quotation marks) will likely be needed when using semi-colon separated entries. |
| `ONEAPI_DEVICE_SELECTOR=opencl:gpu,cpu` | Only CPU and GPU devices on the OpenCL platform are available.|
| `ONEAPI_DEVICE_SELECTOR=opencl:0` | Only the device with index 0 on the OpenCL backend is available. |
| `ONEAPI_DEVICE_SELECTOR=hip:0,2` | Only devices with indices of 0 and 2 from the HIP backend are available. |
| `ONEAPI_DEVICE_SELECTOR=opencl:0.*` | All the sub-devices from the OpenCL device with index 0 are exposed as SYCL root devices. No other devices are available. |
| `ONEAPI_DEVICE_SELECTOR=opencl:0.2` | The third sub-device (2 in zero-based counting) of the OpenCL device with index 0 will be the sole device available. |
| `ONEAPI_DEVICE_SELECTOR=level_zero:*,*.*` | Exposes Level Zero devices to the application in two different ways. Each device (aka "card") is exposed as a SYCL root device and each sub-device is also exposed as a SYCL root device.|


Notes:
- The backend argument is always required. An error will be thrown if it is absent.
- Additionally, the backend MUST be followed by colon ( `:` ) and at least one device specifier of some sort, else an error is thrown.
- For sub-devices and sub-sub-devices, the parent device must support partitioning (`info::partition_property::partition_by_affinity_domain` and `info::partition_affinity_domain::next_partitionable`. See the SYCL 2020 specification for a precise definition.) For Intel GPUs, the sub-device and sub-sub-device syntax can be used to expose tiles or CCSs to the SYCL application as root devices. The exact mapping between sub-device, sub-sub-device, tiles, and CCSs is specific to the hardware.
- The semi-colon character ( `;` ) is treated specially by many shells, so you may need to enclose the string in quotes if the selection string contains this character.



### `SYCL_DEVICE_ALLOWLIST`

A list of devices and their driver version following the pattern:
Expand Down
60 changes: 52 additions & 8 deletions sycl/include/sycl/detail/device_filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,63 @@
#include <sycl/detail/iostream_proxy.hpp>
#include <sycl/info/info_desc.hpp>

#include <optional>
#include <string>

namespace sycl {
__SYCL_INLINE_VER_NAMESPACE(_V1) {
namespace detail {

// ---------------------------------------
// ONEAPI_DEVICE_SELECTOR support

template <typename T>
std::ostream &operator<<(std::ostream &os, std::optional<T> const &opt) {
return opt ? os << opt.value() : os << "not set ";
}

// the ONEAPI_DEVICE_SELECTOR string gets broken down into these targets
// will will match devices.
struct ods_target {
public:
std::optional<backend> Backend;
std::optional<info::device_type> DeviceType;

bool HasDeviceWildCard = false;
std::optional<int> DeviceNum;

bool HasSubDeviceWildCard = false;
std::optional<unsigned> SubDeviceNum;

bool HasSubSubDeviceWildCard = false; // two levels of sub-devices.
std::optional<unsigned> SubSubDeviceNum;

ods_target(backend be) { Backend = be; };
ods_target(){};
friend std::ostream &operator<<(std::ostream &Out, const ods_target &Target);
};

class ods_target_list {
std::vector<ods_target> TargetList;

public:
ods_target_list() {}
ods_target_list(const std::string &FilterString);
std::vector<ods_target> &get() { return TargetList; }
bool containsHost();
bool backendCompatible(backend Backend);
};

std::ostream &operator<<(std::ostream &Out, const ods_target &Target);
std::vector<ods_target> Parse_ONEAPI_DEVICE_SELECTOR(const std::string &envStr);

// ---------------------------------------
// SYCL_DEVICE_FILTER support

struct device_filter {
backend Backend = backend::all;
info::device_type DeviceType = info::device_type::all;
int DeviceNum = 0;
bool HasBackend = false;
bool HasDeviceType = false;
bool HasDeviceNum = false;
std::optional<backend> Backend;
std::optional<info::device_type> DeviceType;
std::optional<int> DeviceNum;
int MatchesSeen = 0;

device_filter(){};
Expand Down Expand Up @@ -66,8 +110,8 @@ inline std::ostream &operator<<(std::ostream &Out,
} else {
Out << "unknown";
}
if (Filter.HasDeviceNum) {
Out << ":" << Filter.DeviceNum;
if (Filter.DeviceNum) {
Out << ":" << Filter.DeviceNum.value();
}
return Out;
}
Expand Down
11 changes: 7 additions & 4 deletions sycl/source/detail/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,22 @@ void dumpConfig() {
#undef CONFIG
}

// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST
const std::array<std::pair<std::string, info::device_type>, 5> &
// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST and
// ONEAPI_DEVICE_SELECTOR
const std::array<std::pair<std::string, info::device_type>, 6> &
getSyclDeviceTypeMap() {
static const std::array<std::pair<std::string, info::device_type>, 5>
static const std::array<std::pair<std::string, info::device_type>, 6>
SyclDeviceTypeMap = {{{"host", info::device_type::host},
{"cpu", info::device_type::cpu},
{"gpu", info::device_type::gpu},
{"acc", info::device_type::accelerator},
{"fpga", info::device_type::accelerator},
{"*", info::device_type::all}}};
return SyclDeviceTypeMap;
}

// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST
// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST and
// ONEAPI_DEVICE_SELECTOR
const std::array<std::pair<std::string, backend>, 7> &getSyclBeMap() {
static const std::array<std::pair<std::string, backend>, 7> SyclBeMap = {
{{"host", backend::host},
Expand Down
1 change: 1 addition & 0 deletions sycl/source/detail/config.def
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ CONFIG(SYCL_ENABLE_DEFAULT_CONTEXTS, 1, __SYCL_ENABLE_DEFAULT_CONTEXTS)
CONFIG(SYCL_QUEUE_THREAD_POOL_SIZE, 4, __SYCL_QUEUE_THREAD_POOL_SIZE)
CONFIG(SYCL_RT_WARNING_LEVEL, 4, __SYCL_RT_WARNING_LEVEL)
CONFIG(SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE, 16, __SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE)
CONFIG(ONEAPI_DEVICE_SELECTOR, 1024, __ONEAPI_DEVICE_SELECTOR)
36 changes: 33 additions & 3 deletions sycl/source/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,43 @@ template <> class SYCLConfig<SYCL_PARALLEL_FOR_RANGE_ROUNDING_PARAMS> {
}
};

// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST
const std::array<std::pair<std::string, info::device_type>, 5> &
// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST and
// ONEAPI_DEVICE_SELECTOR
const std::array<std::pair<std::string, info::device_type>, 6> &
getSyclDeviceTypeMap();

// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST
// Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST and
// ONEAPI_DEVICE_SELECTOR
const std::array<std::pair<std::string, backend>, 7> &getSyclBeMap();

// ---------------------------------------
// ONEAPI_DEVICE_SELECTOR support
template <> class SYCLConfig<ONEAPI_DEVICE_SELECTOR> {
using BaseT = SYCLConfigBase<ONEAPI_DEVICE_SELECTOR>;

public:
static ods_target_list *get() {
// Configuration parameters are processed only once, like reading a string
// from environment and converting it into a typed object.
static bool Initialized = false;
static ods_target_list *DeviceTargets = nullptr;

if (Initialized) {
return DeviceTargets;
}
const char *ValStr = BaseT::getRawValue();
if (ValStr) {
DeviceTargets =
&GlobalHandler::instance().getOneapiDeviceSelectorTargets(ValStr);
}
Initialized = true;
return DeviceTargets;
}
};

// ---------------------------------------
// SYCL_DEVICE_FILTER support

template <> class SYCLConfig<SYCL_DEVICE_FILTER> {
using BaseT = SYCLConfigBase<SYCL_DEVICE_FILTER>;

Expand Down
Loading