Skip to content

Commit 937fec1

Browse files
authored
[SYCL] Support connection with multiple plugins (#1490)
This commit enables including multiple devices of the same device_type and changed the logic of device selection to just prefer a SYCL_BE device if present. If someone uses SYCL_BE but appropriate device is not present, we will simply use any other device. Signed-off-by: Artur Gainullin <[email protected]>
1 parent 04a360a commit 937fec1

File tree

18 files changed

+273
-136
lines changed

18 files changed

+273
-136
lines changed

sycl/doc/EnvironmentVariables.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ subject to change. Do not rely on these variables in production code.
1111

1212
| Environment variable | Values | Description |
1313
| -------------------- | ------ | ----------- |
14-
| SYCL_PI_TRACE | Any(\*) | Force tracing of PI calls to stderr. |
15-
| SYCL_BE | PI_OPENCL, PI_CUDA, PI_OTHER | When SYCL RT is built with PI, this controls which plugin is used by the default device selector. Default value is PI_OPENCL. |
14+
| SYCL_PI_TRACE | Described [below](#sycl_pi_trace-options) | Enable specified level of tracing for PI. |
15+
| SYCL_BE | PI_OPENCL, PI_CUDA | Force SYCL RT to consider only devices of the specified backend during the device selection. |
1616
| 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. |
1717
| SYCL_PROGRAM_COMPILE_OPTIONS | String of valid OpenCL compile options | Override compile options for all programs. |
1818
| SYCL_PROGRAM_LINK_OPTIONS | String of valid OpenCL link options | Override link options for all programs. |
@@ -39,3 +39,12 @@ SYCL_PRINT_EXECUTION_GRAPH can accept one or more comma separated values from th
3939
| after_addHostAcc | print graph after addHostAccessor method |
4040
| always | print graph before and after each of the above methods |
4141

42+
### SYCL_PI_TRACE Options
43+
44+
SYCL_PI_TRACE accepts a bit-mask. Supported tracing levels are in the table below
45+
46+
| Option | Description |
47+
| ------ | ----------- |
48+
| 1 | Enable basic tracing, which is tracing of PI plugins/devices discovery |
49+
| 2 | Enable tracing of the PI calls |
50+
| -1 | Enable all levels of tracing |

sycl/include/CL/sycl/detail/pi.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#pragma once
1515

16+
#include <CL/sycl/backend_types.hpp>
1617
#include <CL/sycl/detail/common.hpp>
1718
#include <CL/sycl/detail/export.hpp>
1819
#include <CL/sycl/detail/os_util.hpp>
@@ -43,6 +44,17 @@ enum class PiApiKind {
4344
class plugin;
4445
namespace pi {
4546

47+
// The SYCL_PI_TRACE sets what we will trace.
48+
// This is a bit-mask of various things we'd want to trace.
49+
enum TraceLevel {
50+
PI_TRACE_BASIC = 0x1,
51+
PI_TRACE_CALLS = 0x2,
52+
PI_TRACE_ALL = -1
53+
};
54+
55+
// Return true if we want to trace PI related activities.
56+
bool trace(TraceLevel level);
57+
4658
#ifdef SYCL_RT_OS_WINDOWS
4759
#define OPENCL_PLUGIN_NAME "pi_opencl.dll"
4860
#define CUDA_PLUGIN_NAME "pi_cuda.dll"
@@ -111,13 +123,6 @@ void *loadOsLibrary(const std::string &Library);
111123
// library, implementation is OS dependent.
112124
void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName);
113125

114-
// For selection of SYCL RT back-end, now manually through the "SYCL_BE"
115-
// environment variable.
116-
enum Backend { SYCL_BE_PI_OPENCL, SYCL_BE_PI_CUDA, SYCL_BE_PI_OTHER };
117-
118-
// Check for manually selected BE at run-time.
119-
bool useBackend(Backend Backend);
120-
121126
// Get a string representing a _pi_platform_info enum
122127
std::string platformInfoToString(pi_platform_info info);
123128

sycl/source/detail/config.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ void readConfig() {
103103
void dumpConfig() {
104104
#define CONFIG(Name, MaxSize, CompileTimeDef) \
105105
{ \
106-
const char *Val = SYCLConfig<Name>::get(); \
106+
const char *Val = SYCLConfigBase<Name>::getRawValue(); \
107107
std::cerr << SYCLConfigBase<Name>::MConfigName << " : " \
108108
<< (Val ? Val : "unset") << std::endl; \
109109
}

sycl/source/detail/config.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
CONFIG(SYCL_PRINT_EXECUTION_GRAPH, 32, __SYCL_PRINT_EXECUTION_GRAPH)
1414
CONFIG(SYCL_DISABLE_EXECUTION_GRAPH_CLEANUP, 1, __SYCL_DISABLE_EXECUTION_GRAPH_CLEANUP)
1515
CONFIG(SYCL_DEVICE_ALLOWLIST, 1024, __SYCL_DEVICE_ALLOWLIST)
16+
CONFIG(SYCL_BE, 16, __SYCL_BE)
17+
CONFIG(SYCL_PI_TRACE, 16, __SYCL_PI_TRACE)

sycl/source/detail/config.hpp

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99
#pragma once
1010

11+
#include <CL/sycl/backend_types.hpp>
1112
#include <CL/sycl/detail/defines.hpp>
13+
#include <CL/sycl/detail/pi.hpp>
1214

15+
#include <algorithm>
16+
#include <array>
1317
#include <cstdlib>
18+
#include <utility>
1419

1520
__SYCL_INLINE_NAMESPACE(cl) {
1621
namespace sycl {
@@ -48,6 +53,9 @@ constexpr const char *getStrOrNullptr(const char *Str) {
4853
return (Str[0] == '_' && Str[1] == '_') ? nullptr : Str;
4954
}
5055

56+
// Intializes configs from the configuration file
57+
void readConfig();
58+
5159
template <ConfigID Config> class SYCLConfigBase;
5260

5361
#define CONFIG(Name, MaxSize, CompileTimeDef) \
@@ -65,38 +73,89 @@ template <ConfigID Config> class SYCLConfigBase;
6573
* beginning of the string, if it starts with double underscore(__) the \
6674
* value is not set.*/ \
6775
static const char *const MCompileTimeDef; \
76+
\
77+
static const char *getRawValue() { \
78+
if (ConfigFromEnvEnabled) \
79+
if (const char *ValStr = getenv(MConfigName)) \
80+
return ValStr; \
81+
\
82+
if (ConfigFromFileEnabled) { \
83+
readConfig(); \
84+
if (MValueFromFile) \
85+
return MValueFromFile; \
86+
} \
87+
\
88+
if (ConfigFromCompileDefEnabled && MCompileTimeDef) \
89+
return MCompileTimeDef; \
90+
\
91+
return nullptr; \
92+
} \
6893
};
6994
#include "config.def"
7095
#undef CONFIG
7196

72-
// Intializes configs from the configuration file
73-
void readConfig();
74-
7597
template <ConfigID Config> class SYCLConfig {
7698
using BaseT = SYCLConfigBase<Config>;
7799

78100
public:
79101
static const char *get() {
80-
const char *ValStr = getRawValue();
102+
static const char *ValStr = BaseT::getRawValue();
81103
return ValStr;
82104
}
105+
};
83106

84-
private:
85-
static const char *getRawValue() {
86-
if (ConfigFromEnvEnabled)
87-
if (const char *ValStr = getenv(BaseT::MConfigName))
88-
return ValStr;
107+
template <> class SYCLConfig<SYCL_BE> {
108+
using BaseT = SYCLConfigBase<SYCL_BE>;
89109

90-
if (ConfigFromFileEnabled) {
91-
readConfig();
92-
if (BaseT::MValueFromFile)
93-
return BaseT::MValueFromFile;
110+
public:
111+
static backend *get() {
112+
static bool Initialized = false;
113+
static backend *BackendPtr = nullptr;
114+
115+
// Configuration parameters are processed only once, like reading a string
116+
// from environment and converting it into a typed object.
117+
if (Initialized)
118+
return BackendPtr;
119+
120+
const char *ValStr = BaseT::getRawValue();
121+
const std::array<std::pair<std::string, backend>, 2> SyclBeMap = {
122+
{{"PI_OPENCL", backend::opencl}, {"PI_CUDA", backend::cuda}}};
123+
if (ValStr) {
124+
auto It = std::find_if(
125+
std::begin(SyclBeMap), std::end(SyclBeMap),
126+
[&ValStr](const std::pair<std::string, backend> &element) {
127+
return element.first == ValStr;
128+
});
129+
if (It == SyclBeMap.end())
130+
pi::die("Invalid backend. "
131+
"Valid values are PI_OPENCL/PI_CUDA");
132+
static backend Backend = It->second;
133+
BackendPtr = &Backend;
94134
}
135+
Initialized = true;
136+
return BackendPtr;
137+
}
138+
};
95139

96-
if (ConfigFromCompileDefEnabled && BaseT::MCompileTimeDef)
97-
return BaseT::MCompileTimeDef;
140+
template <> class SYCLConfig<SYCL_PI_TRACE> {
141+
using BaseT = SYCLConfigBase<SYCL_PI_TRACE>;
98142

99-
return nullptr;
143+
public:
144+
static int get() {
145+
static bool Initialized = false;
146+
// We don't use TraceLevel enum here because user can provide any bitmask
147+
// which can correspond to several enum values.
148+
static int Level = 0; // No tracing by default
149+
150+
// Configuration parameters are processed only once, like reading a string
151+
// from environment and converting it into a typed object.
152+
if (Initialized)
153+
return Level;
154+
155+
const char *ValStr = BaseT::getRawValue();
156+
Level = (ValStr ? std::atoi(ValStr) : 0);
157+
Initialized = true;
158+
return Level;
100159
}
101160
};
102161

sycl/source/detail/pi.cpp

Lines changed: 45 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515
#include <CL/sycl/context.hpp>
1616
#include <CL/sycl/detail/common.hpp>
1717
#include <CL/sycl/detail/pi.hpp>
18+
#include <detail/config.hpp>
1819
#include <detail/plugin.hpp>
1920

2021
#include <bitset>
2122
#include <cstdarg>
2223
#include <cstring>
2324
#include <iostream>
2425
#include <map>
26+
#include <sstream>
2527
#include <stddef.h>
2628
#include <string>
27-
#include <sstream>
2829

2930
#ifdef XPTI_ENABLE_INSTRUMENTATION
3031
// Include the headers necessary for emitting
@@ -141,39 +142,21 @@ std::string memFlagsToString(pi_mem_flags Flags) {
141142
return Sstream.str();
142143
}
143144

144-
// Check for manually selected BE at run-time.
145-
static Backend getBackend() {
146-
static const char *GetEnv = std::getenv("SYCL_BE");
147-
// Current default backend as SYCL_BE_PI_OPENCL
148-
// Valid values of GetEnv are "PI_OPENCL", "PI_CUDA" and "PI_OTHER"
149-
std::string StringGetEnv = (GetEnv ? GetEnv : "PI_OPENCL");
150-
static const Backend Use =
151-
std::map<std::string, Backend>{
152-
{ "PI_OPENCL", SYCL_BE_PI_OPENCL },
153-
{ "PI_CUDA", SYCL_BE_PI_CUDA },
154-
{ "PI_OTHER", SYCL_BE_PI_OTHER }
155-
}[ GetEnv ? StringGetEnv : "PI_OPENCL"];
156-
return Use;
157-
}
158-
159-
// Check for manually selected BE at run-time.
160-
bool useBackend(Backend TheBackend) {
161-
return TheBackend == getBackend();
162-
}
163-
164145
// GlobalPlugin is a global Plugin used with Interoperability constructors that
165146
// use OpenCL objects to construct SYCL class objects.
166147
std::shared_ptr<plugin> GlobalPlugin;
167148

168149
// Find the plugin at the appropriate location and return the location.
169-
// TODO: Change the function appropriately when there are multiple plugins.
170-
bool findPlugins(vector_class<std::string> &PluginNames) {
150+
bool findPlugins(vector_class<std::pair<std::string, backend>> &PluginNames) {
171151
// TODO: Based on final design discussions, change the location where the
172152
// plugin must be searched; how to identify the plugins etc. Currently the
173153
// search is done for libpi_opencl.so/pi_opencl.dll file in LD_LIBRARY_PATH
174154
// env only.
175-
PluginNames.push_back(OPENCL_PLUGIN_NAME);
176-
PluginNames.push_back(CUDA_PLUGIN_NAME);
155+
//
156+
PluginNames.push_back(std::make_pair<std::string, backend>(OPENCL_PLUGIN_NAME,
157+
backend::opencl));
158+
PluginNames.push_back(
159+
std::make_pair<std::string, backend>(CUDA_PLUGIN_NAME, backend::cuda));
177160
return true;
178161
}
179162

@@ -207,52 +190,59 @@ bool bindPlugin(void *Library, PiPlugin *PluginInformation) {
207190
return true;
208191
}
209192

210-
// Load the plugin based on SYCL_BE.
211-
// TODO: Currently only accepting OpenCL and CUDA plugins. Edit it to identify
212-
// and load other kinds of plugins, do the required changes in the
213-
// findPlugins, loadPlugin and bindPlugin functions.
193+
bool trace(TraceLevel Level) {
194+
auto TraceLevelMask = SYCLConfig<SYCL_PI_TRACE>::get();
195+
return (TraceLevelMask & Level) == Level;
196+
}
197+
198+
// Initializes all available Plugins.
214199
vector_class<plugin> initialize() {
215200
vector_class<plugin> Plugins;
216-
217-
if (!useBackend(SYCL_BE_PI_OPENCL) && !useBackend(SYCL_BE_PI_CUDA)) {
218-
die("Unknown SYCL_BE");
219-
}
220-
221-
bool EnableTrace = (std::getenv("SYCL_PI_TRACE") != nullptr);
222-
223-
vector_class<std::string> PluginNames;
201+
vector_class<std::pair<std::string, backend>> PluginNames;
224202
findPlugins(PluginNames);
225203

226-
if (PluginNames.empty() && EnableTrace)
227-
std::cerr << "No Plugins Found." << std::endl;
204+
if (PluginNames.empty() && trace(PI_TRACE_ALL))
205+
std::cerr << "SYCL_PI_TRACE[all]: "
206+
<< "No Plugins Found." << std::endl;
228207

229-
PiPlugin PluginInformation; // TODO: include.
208+
PiPlugin PluginInformation;
230209
for (unsigned int I = 0; I < PluginNames.size(); I++) {
231-
void *Library = loadPlugin(PluginNames[I]);
210+
void *Library = loadPlugin(PluginNames[I].first);
232211

233212
if (!Library) {
234-
if (EnableTrace) {
235-
std::cerr << "Check if plugin is present. Failed to load plugin: "
236-
<< PluginNames[I] << std::endl;
213+
if (trace(PI_TRACE_ALL)) {
214+
std::cerr << "SYCL_PI_TRACE[all]: "
215+
<< "Check if plugin is present. "
216+
<< "Failed to load plugin: " << PluginNames[I].first
217+
<< std::endl;
237218
}
238219
continue;
239220
}
240221

241-
if (!bindPlugin(Library, &PluginInformation) && EnableTrace) {
242-
std::cerr << "Failed to bind PI APIs to the plugin: " << PluginNames[I]
243-
<< std::endl;
222+
if (!bindPlugin(Library, &PluginInformation)) {
223+
if (trace(PI_TRACE_ALL)) {
224+
std::cerr << "SYCL_PI_TRACE[all]: "
225+
<< "Failed to bind PI APIs to the plugin: "
226+
<< PluginNames[I].first << std::endl;
227+
}
228+
continue;
244229
}
245-
if (useBackend(SYCL_BE_PI_OPENCL) &&
246-
PluginNames[I].find("opencl") != std::string::npos) {
230+
backend *BE = SYCLConfig<SYCL_BE>::get();
231+
if (!BE || (*BE == backend::opencl &&
232+
PluginNames[I].first.find("opencl") != std::string::npos)) {
247233
// Use the OpenCL plugin as the GlobalPlugin
248-
GlobalPlugin = std::make_shared<plugin>(PluginInformation);
249-
}
250-
if (useBackend(SYCL_BE_PI_CUDA) &&
251-
PluginNames[I].find("cuda") != std::string::npos) {
234+
GlobalPlugin =
235+
std::make_shared<plugin>(PluginInformation, backend::opencl);
236+
} else if (*BE == backend::cuda &&
237+
PluginNames[I].first.find("cuda") != std::string::npos) {
252238
// Use the CUDA plugin as the GlobalPlugin
253-
GlobalPlugin = std::make_shared<plugin>(PluginInformation);
239+
GlobalPlugin = std::make_shared<plugin>(PluginInformation, backend::cuda);
254240
}
255-
Plugins.push_back(plugin(PluginInformation));
241+
Plugins.emplace_back(plugin(PluginInformation, PluginNames[I].second));
242+
if (trace(TraceLevel::PI_TRACE_BASIC))
243+
std::cerr << "SYCL_PI_TRACE[basic]: "
244+
<< "Plugin found and successfully loaded: "
245+
<< PluginNames[I].first << std::endl;
256246
}
257247

258248
#ifdef XPTI_ENABLE_INSTRUMENTATION

0 commit comments

Comments
 (0)