Skip to content

Commit

Permalink
[SYCL] Add ONEAPI_DEVICE_SELECTOR implementation (#6779)
Browse files Browse the repository at this point in the history
Functionally complete. Needs tests and possible reorganization.

Signed-off-by: Chris Perkins <chris.perkins@intel.com>
  • Loading branch information
cperkinsintel authored Oct 6, 2022
1 parent 1c3d598 commit 28d0cd3
Show file tree
Hide file tree
Showing 14 changed files with 639 additions and 94 deletions.
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

0 comments on commit 28d0cd3

Please sign in to comment.