Skip to content

[SYCL] Support connection with multiple plugins #1490

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 6 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 11 additions & 2 deletions sycl/doc/EnvironmentVariables.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ subject to change. Do not rely on these variables in production code.

| Environment variable | Values | Description |
| -------------------- | ------ | ----------- |
| SYCL_PI_TRACE | Any(\*) | Force tracing of PI calls to stderr. |
| 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. |
| SYCL_PI_TRACE | Described [below](#sycl_pi_trace-options) | Enable specified level of tracing for PI. |
| SYCL_BE | PI_OPENCL, PI_CUDA | Force SYCL RT to consider only devices of the specified backend during the device selection. |
| 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. |
| SYCL_PROGRAM_COMPILE_OPTIONS | String of valid OpenCL compile options | Override compile options for all programs. |
| SYCL_PROGRAM_LINK_OPTIONS | String of valid OpenCL link options | Override link options for all programs. |
Expand All @@ -39,3 +39,12 @@ SYCL_PRINT_EXECUTION_GRAPH can accept one or more comma separated values from th
| after_addHostAcc | print graph after addHostAccessor method |
| always | print graph before and after each of the above methods |

### SYCL_PI_TRACE Options

SYCL_PI_TRACE accepts a bit-mask. Supported tracing levels are in the table below

| Option | Description |
| ------ | ----------- |
| 1 | Enable basic tracing, which is tracing of PI plugins/devices discovery |
| 2 | Enable tracing of the PI calls |
| -1 | Enable all levels of tracing |
19 changes: 12 additions & 7 deletions sycl/include/CL/sycl/detail/pi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#pragma once

#include <CL/sycl/backend_types.hpp>
#include <CL/sycl/detail/common.hpp>
#include <CL/sycl/detail/export.hpp>
#include <CL/sycl/detail/os_util.hpp>
Expand Down Expand Up @@ -43,6 +44,17 @@ enum class PiApiKind {
class plugin;
namespace pi {

// The SYCL_PI_TRACE sets what we will trace.
// This is a bit-mask of various things we'd want to trace.
enum TraceLevel {
PI_TRACE_BASIC = 0x1,
PI_TRACE_CALLS = 0x2,
PI_TRACE_ALL = -1
};

// Return true if we want to trace PI related activities.
bool trace(TraceLevel level);

#ifdef SYCL_RT_OS_WINDOWS
#define OPENCL_PLUGIN_NAME "pi_opencl.dll"
#define CUDA_PLUGIN_NAME "pi_cuda.dll"
Expand Down Expand Up @@ -111,13 +123,6 @@ void *loadOsLibrary(const std::string &Library);
// library, implementation is OS dependent.
void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName);

// For selection of SYCL RT back-end, now manually through the "SYCL_BE"
// environment variable.
enum Backend { SYCL_BE_PI_OPENCL, SYCL_BE_PI_CUDA, SYCL_BE_PI_OTHER };

// Check for manually selected BE at run-time.
bool useBackend(Backend Backend);

// Get a string representing a _pi_platform_info enum
std::string platformInfoToString(pi_platform_info info);

Expand Down
2 changes: 1 addition & 1 deletion sycl/source/detail/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void readConfig() {
void dumpConfig() {
#define CONFIG(Name, MaxSize, CompileTimeDef) \
{ \
const char *Val = SYCLConfig<Name>::get(); \
const char *Val = SYCLConfigBase<Name>::getRawValue(); \
std::cerr << SYCLConfigBase<Name>::MConfigName << " : " \
<< (Val ? Val : "unset") << std::endl; \
}
Expand Down
2 changes: 2 additions & 0 deletions sycl/source/detail/config.def
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
CONFIG(SYCL_PRINT_EXECUTION_GRAPH, 32, __SYCL_PRINT_EXECUTION_GRAPH)
CONFIG(SYCL_DISABLE_EXECUTION_GRAPH_CLEANUP, 1, __SYCL_DISABLE_EXECUTION_GRAPH_CLEANUP)
CONFIG(SYCL_DEVICE_ALLOWLIST, 1024, __SYCL_DEVICE_ALLOWLIST)
CONFIG(SYCL_BE, 16, __SYCL_BE)
CONFIG(SYCL_PI_TRACE, 16, __SYCL_PI_TRACE)
91 changes: 75 additions & 16 deletions sycl/source/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@

#pragma once

#include <CL/sycl/backend_types.hpp>
#include <CL/sycl/detail/defines.hpp>
#include <CL/sycl/detail/pi.hpp>

#include <algorithm>
#include <array>
#include <cstdlib>
#include <utility>

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

// Intializes configs from the configuration file
void readConfig();

template <ConfigID Config> class SYCLConfigBase;

#define CONFIG(Name, MaxSize, CompileTimeDef) \
Expand All @@ -65,38 +73,89 @@ template <ConfigID Config> class SYCLConfigBase;
* beginning of the string, if it starts with double underscore(__) the \
* value is not set.*/ \
static const char *const MCompileTimeDef; \
\
static const char *getRawValue() { \
if (ConfigFromEnvEnabled) \
if (const char *ValStr = getenv(MConfigName)) \
return ValStr; \
\
if (ConfigFromFileEnabled) { \
readConfig(); \
if (MValueFromFile) \
return MValueFromFile; \
} \
\
if (ConfigFromCompileDefEnabled && MCompileTimeDef) \
return MCompileTimeDef; \
\
return nullptr; \
} \
};
#include "config.def"
#undef CONFIG

// Intializes configs from the configuration file
void readConfig();

template <ConfigID Config> class SYCLConfig {
using BaseT = SYCLConfigBase<Config>;

public:
static const char *get() {
const char *ValStr = getRawValue();
static const char *ValStr = BaseT::getRawValue();
return ValStr;
}
};

private:
static const char *getRawValue() {
if (ConfigFromEnvEnabled)
if (const char *ValStr = getenv(BaseT::MConfigName))
return ValStr;
template <> class SYCLConfig<SYCL_BE> {
using BaseT = SYCLConfigBase<SYCL_BE>;

if (ConfigFromFileEnabled) {
readConfig();
if (BaseT::MValueFromFile)
return BaseT::MValueFromFile;
public:
static backend *get() {
static bool Initialized = false;
static backend *BackendPtr = nullptr;

// Configuration parameters are processed only once, like reading a string
// from environment and converting it into a typed object.
if (Initialized)
return BackendPtr;

const char *ValStr = BaseT::getRawValue();
const std::array<std::pair<std::string, backend>, 2> SyclBeMap = {
{{"PI_OPENCL", backend::opencl}, {"PI_CUDA", backend::cuda}}};
if (ValStr) {
auto It = std::find_if(
std::begin(SyclBeMap), std::end(SyclBeMap),
[&ValStr](const std::pair<std::string, backend> &element) {
return element.first == ValStr;
});
if (It == SyclBeMap.end())
pi::die("Invalid backend. "
"Valid values are PI_OPENCL/PI_CUDA");
static backend Backend = It->second;
BackendPtr = &Backend;
}
Initialized = true;
return BackendPtr;
}
};

if (ConfigFromCompileDefEnabled && BaseT::MCompileTimeDef)
return BaseT::MCompileTimeDef;
template <> class SYCLConfig<SYCL_PI_TRACE> {
using BaseT = SYCLConfigBase<SYCL_PI_TRACE>;

return nullptr;
public:
static int get() {
static bool Initialized = false;
// We don't use TraceLevel enum here because user can provide any bitmask
// which can correspond to several enum values.
static int Level = 0; // No tracing by default

// Configuration parameters are processed only once, like reading a string
// from environment and converting it into a typed object.
if (Initialized)
return Level;

const char *ValStr = BaseT::getRawValue();
Level = (ValStr ? std::atoi(ValStr) : 0);
Initialized = true;
return Level;
}
};

Expand Down
100 changes: 45 additions & 55 deletions sycl/source/detail/pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
#include <CL/sycl/context.hpp>
#include <CL/sycl/detail/common.hpp>
#include <CL/sycl/detail/pi.hpp>
#include <detail/config.hpp>
#include <detail/plugin.hpp>

#include <bitset>
#include <cstdarg>
#include <cstring>
#include <iostream>
#include <map>
#include <sstream>
#include <stddef.h>
#include <string>
#include <sstream>

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

// Check for manually selected BE at run-time.
static Backend getBackend() {
static const char *GetEnv = std::getenv("SYCL_BE");
// Current default backend as SYCL_BE_PI_OPENCL
// Valid values of GetEnv are "PI_OPENCL", "PI_CUDA" and "PI_OTHER"
std::string StringGetEnv = (GetEnv ? GetEnv : "PI_OPENCL");
static const Backend Use =
std::map<std::string, Backend>{
{ "PI_OPENCL", SYCL_BE_PI_OPENCL },
{ "PI_CUDA", SYCL_BE_PI_CUDA },
{ "PI_OTHER", SYCL_BE_PI_OTHER }
}[ GetEnv ? StringGetEnv : "PI_OPENCL"];
return Use;
}

// Check for manually selected BE at run-time.
bool useBackend(Backend TheBackend) {
return TheBackend == getBackend();
}

// GlobalPlugin is a global Plugin used with Interoperability constructors that
// use OpenCL objects to construct SYCL class objects.
std::shared_ptr<plugin> GlobalPlugin;

// Find the plugin at the appropriate location and return the location.
// TODO: Change the function appropriately when there are multiple plugins.
bool findPlugins(vector_class<std::string> &PluginNames) {
bool findPlugins(vector_class<std::pair<std::string, backend>> &PluginNames) {
// TODO: Based on final design discussions, change the location where the
// plugin must be searched; how to identify the plugins etc. Currently the
// search is done for libpi_opencl.so/pi_opencl.dll file in LD_LIBRARY_PATH
// env only.
PluginNames.push_back(OPENCL_PLUGIN_NAME);
PluginNames.push_back(CUDA_PLUGIN_NAME);
//
PluginNames.push_back(std::make_pair<std::string, backend>(OPENCL_PLUGIN_NAME,
backend::opencl));
PluginNames.push_back(
std::make_pair<std::string, backend>(CUDA_PLUGIN_NAME, backend::cuda));
return true;
}

Expand Down Expand Up @@ -207,52 +190,59 @@ bool bindPlugin(void *Library, PiPlugin *PluginInformation) {
return true;
}

// Load the plugin based on SYCL_BE.
// TODO: Currently only accepting OpenCL and CUDA plugins. Edit it to identify
// and load other kinds of plugins, do the required changes in the
// findPlugins, loadPlugin and bindPlugin functions.
bool trace(TraceLevel Level) {
auto TraceLevelMask = SYCLConfig<SYCL_PI_TRACE>::get();
return (TraceLevelMask & Level) == Level;
}

// Initializes all available Plugins.
vector_class<plugin> initialize() {
vector_class<plugin> Plugins;

if (!useBackend(SYCL_BE_PI_OPENCL) && !useBackend(SYCL_BE_PI_CUDA)) {
die("Unknown SYCL_BE");
}

bool EnableTrace = (std::getenv("SYCL_PI_TRACE") != nullptr);

vector_class<std::string> PluginNames;
vector_class<std::pair<std::string, backend>> PluginNames;
findPlugins(PluginNames);

if (PluginNames.empty() && EnableTrace)
std::cerr << "No Plugins Found." << std::endl;
if (PluginNames.empty() && trace(PI_TRACE_ALL))
std::cerr << "SYCL_PI_TRACE[all]: "
<< "No Plugins Found." << std::endl;

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

if (!Library) {
if (EnableTrace) {
std::cerr << "Check if plugin is present. Failed to load plugin: "
<< PluginNames[I] << std::endl;
if (trace(PI_TRACE_ALL)) {
std::cerr << "SYCL_PI_TRACE[all]: "
<< "Check if plugin is present. "
<< "Failed to load plugin: " << PluginNames[I].first
<< std::endl;
}
continue;
}

if (!bindPlugin(Library, &PluginInformation) && EnableTrace) {
std::cerr << "Failed to bind PI APIs to the plugin: " << PluginNames[I]
<< std::endl;
if (!bindPlugin(Library, &PluginInformation)) {
if (trace(PI_TRACE_ALL)) {
std::cerr << "SYCL_PI_TRACE[all]: "
<< "Failed to bind PI APIs to the plugin: "
<< PluginNames[I].first << std::endl;
}
continue;
}
if (useBackend(SYCL_BE_PI_OPENCL) &&
PluginNames[I].find("opencl") != std::string::npos) {
backend *BE = SYCLConfig<SYCL_BE>::get();
if (!BE || (*BE == backend::opencl &&
PluginNames[I].first.find("opencl") != std::string::npos)) {
// Use the OpenCL plugin as the GlobalPlugin
GlobalPlugin = std::make_shared<plugin>(PluginInformation);
}
if (useBackend(SYCL_BE_PI_CUDA) &&
PluginNames[I].find("cuda") != std::string::npos) {
GlobalPlugin =
std::make_shared<plugin>(PluginInformation, backend::opencl);
} else if (*BE == backend::cuda &&
PluginNames[I].first.find("cuda") != std::string::npos) {
// Use the CUDA plugin as the GlobalPlugin
GlobalPlugin = std::make_shared<plugin>(PluginInformation);
GlobalPlugin = std::make_shared<plugin>(PluginInformation, backend::cuda);
}
Plugins.push_back(plugin(PluginInformation));
Plugins.emplace_back(plugin(PluginInformation, PluginNames[I].second));
if (trace(TraceLevel::PI_TRACE_BASIC))
std::cerr << "SYCL_PI_TRACE[basic]: "
<< "Plugin found and successfully loaded: "
<< PluginNames[I].first << std::endl;
}

#ifdef XPTI_ENABLE_INSTRUMENTATION
Expand Down
Loading