Skip to content

[SYCL][L0] Implement Plugin Lifetime Management #2942

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 29 commits into from
Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
88354e0
[SYCL][L0] Implement Plugin Lifetime Management
bso-intel Dec 23, 2020
3db0a7b
Fixed errors
bso-intel Dec 23, 2020
50fb112
Fix windows casting error
bso-intel Dec 23, 2020
db8ee8c
uplift minor ABI version due to non-breaking change
bso-intel Dec 23, 2020
bebcd64
typo
bso-intel Dec 23, 2020
43eb187
added plugin lifetime design overview in doc
bso-intel Dec 23, 2020
d0d2a5d
Update sycl/doc/GlobalObjectsInRuntime.md
bso-intel Dec 24, 2020
1831f10
Update sycl/include/CL/sycl/detail/pi.h
bso-intel Dec 24, 2020
52a3887
Update sycl/source/detail/global_handler.cpp
bso-intel Dec 24, 2020
f49ad91
Update sycl/include/CL/sycl/detail/pi.h
bso-intel Dec 24, 2020
7c283af
Respond to feedbacks
bso-intel Dec 24, 2020
1d83fd4
Change var plgn to Plugin
bso-intel Dec 24, 2020
f17070f
add a placeholder parameter to piTearDown
bso-intel Dec 24, 2020
ebeec30
clang-format
bso-intel Dec 24, 2020
aa5a252
fixed wrong function to add param
bso-intel Dec 24, 2020
5ae6900
added placeholder parameter to piteardown
bso-intel Dec 24, 2020
4fa8ef5
clang-format
bso-intel Dec 24, 2020
25e1f6d
Update sycl/plugins/level_zero/pi_level_zero.cpp
bso-intel Dec 25, 2020
f6e51ad
Update sycl/doc/GlobalObjectsInRuntime.md
bso-intel Dec 25, 2020
3386693
Update sycl/doc/GlobalObjectsInRuntime.md
bso-intel Dec 25, 2020
f1f1cd2
Merge branch 'sycl' into plugin-lifetime
bso-intel Dec 25, 2020
bcc6a76
respond to further feedback
bso-intel Dec 25, 2020
633779d
Update sycl/plugins/level_zero/pi_level_zero.cpp
bso-intel Dec 28, 2020
975331e
Update sycl/plugins/level_zero/pi_level_zero.cpp
bso-intel Dec 28, 2020
370fc34
redrew diagram of plugin lifetime
bso-intel Dec 28, 2020
2528756
Update sycl/doc/GlobalObjectsInRuntime.md
bso-intel Dec 29, 2020
77a5c73
Fixed the diagram labels.
bso-intel Dec 29, 2020
f26f54e
Merge branch 'plugin-lifetime' of https://github.com/bso-intel/llvm i…
bso-intel Dec 29, 2020
63d0a4c
missed picture
bso-intel Dec 29, 2020
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
9 changes: 8 additions & 1 deletion sycl/doc/GlobalObjectsInRuntime.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ constructor and destructor.

## Plugins

TBD
Plugin lifetime is managed by utilizing piPluginInit() and piTearDown().
GlobalHandler::shutdown() will tear down all registered globals before SYCL RT
library is unloaded. It will invoke piTearDown() and unload() for each
plugin. piTearDown() is going to perform any necessary tear-down process at the
plugin PI level. These two APIs allow on-demand plugin lifetime management. SYCL
RT can control the beginning and the end of the plugin.

![](images/plugin-lifetime.jpg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add arrow with piPluginInit label, which goes from "Plugin Interface" to "Plugin", it should correspond to "bind plugin" arrow?
Also, could you please rename "destory plugin" to piTearDown?


## Low-level runtimes

Expand Down
19 changes: 16 additions & 3 deletions sycl/doc/PluginInterface.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,22 @@ The trace shows the PI API calls made when using SYCL_PI_TRACE=-1.
bound.)

### Plugin Unloading
The plugins not chosen to be connected to should be unloaded.

TBD - Unloading a bound plugin.
The plugins not chosen to be connected to should be unloaded. piInitializePlugins()
can be called to load and bound the necessary plugins. In addition, piTearDown()
can be called when plugins are not needed any more. It notifies each
plugin to start performing its own tear-down process such as global memory
deallocation. In the future, piTearDown() can include any other jobs that need to
be done before the plugin is unloaded from memory. Possibly, a
notification of the plugin unloading to lower-level plugins can be added so that
they can clean up their own memory [TBD].
After piTearDown() is called, the plugin can be safely unloaded by calling unload(),
which is going to invoke OS-specific system calls to remove the dynamic library
from memory.

Each plugin should not create global variables that require non-trivial
destructor. Pointer variables with heap memory allocation is a good example
to be created at the global scope. A std::vector object is not. piTearDown
will take care of deallocation of these global variables safely.

## PI API Specification

Expand Down
Binary file added sycl/doc/images/plugin-lifetime.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions sycl/include/CL/sycl/detail/pi.def
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,6 @@ _PI_API(piextUSMGetMemAllocInfo)

_PI_API(piextKernelSetArgMemObj)
_PI_API(piextKernelSetArgSampler)
_PI_API(piTearDown)

#undef _PI_API
5 changes: 5 additions & 0 deletions sycl/include/CL/sycl/detail/pi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,11 @@ __SYCL_EXPORT pi_result piextUSMGetMemAllocInfo(
pi_context context, const void *ptr, pi_mem_info param_name,
size_t param_value_size, void *param_value, size_t *param_value_size_ret);

/// API to notify that the plugin should clean up its resources.
/// No PI calls should be made until the next piPluginInit call.
/// \param PluginParameter placeholder for future use, currenly not used.
__SYCL_EXPORT pi_result piTearDown(void *PluginParameter);

struct _pi_plugin {
// PI version supported by host passed to the plugin. The Plugin
// checks and writes the appropriate Function Pointers in
Expand Down
7 changes: 7 additions & 0 deletions sycl/include/CL/sycl/detail/pi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ __SYCL_EXPORT void contextSetExtendedDeleter(const cl::sycl::context &constext,
// Implementation is OS dependent.
void *loadOsLibrary(const std::string &Library);

// Function to unload the shared library
// Implementation is OS dependent (see posix-pi.cpp and windows-pi.cpp)
int unloadOsLibrary(void *Library);

// OS agnostic function to unload the shared library
int unloadPlugin(void *Library);

// Function to get Address of a symbol defined in the shared
// library, implementation is OS dependent.
void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName);
Expand Down
6 changes: 6 additions & 0 deletions sycl/plugins/cuda/pi_cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4475,6 +4475,11 @@ pi_result cuda_piextUSMGetMemAllocInfo(pi_context context, const void *ptr,
return result;
}

// This API is called by Sycl RT to notify the end of the plugin lifetime.
// TODO: add a global variable lifetime management code here (see
// pi_level_zero.cpp for reference) Currently this is just a NOOP.
pi_result cuda_piTearDown(void *PluginParameter) { return PI_SUCCESS; }

const char SupportedVersion[] = _PI_H_VERSION_STRING;

pi_result piPluginInit(pi_plugin *PluginInit) {
Expand Down Expand Up @@ -4610,6 +4615,7 @@ pi_result piPluginInit(pi_plugin *PluginInit) {

_PI_CL(piextKernelSetArgMemObj, cuda_piextKernelSetArgMemObj)
_PI_CL(piextKernelSetArgSampler, cuda_piextKernelSetArgSampler)
_PI_CL(piTearDown, cuda_piTearDown)

#undef _PI_CL

Expand Down
38 changes: 28 additions & 10 deletions sycl/plugins/level_zero/pi_level_zero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/// \ingroup sycl_pi_level_zero

#include "pi_level_zero.hpp"
#include <CL/sycl/detail/spinlock.hpp>
#include <algorithm>
#include <cstdarg>
#include <cstdio>
Expand Down Expand Up @@ -172,6 +173,17 @@ class ReturnHelper {

} // anonymous namespace

// Global variables used in PI_Level_Zero
// Note we only create a simple pointer variables such that C++ RT won't
// deallocate them automatically at the end of the main program.
// The heap memory allocated for these global variables reclaimed only when
// Sycl RT calls piTearDown().
static std::vector<pi_platform> *PiPlatformsCache =
new std::vector<pi_platform>;
static sycl::detail::SpinLock *PiPlatformsCacheMutex =
new sycl::detail::SpinLock;
static bool PiPlatformCachePopulated = false;

// TODO:: In the following 4 methods we may want to distinguish read access vs.
// write (as it is OK for multiple threads to read the map without locking it).

Expand Down Expand Up @@ -821,16 +833,8 @@ pi_result piPlatformsGet(pi_uint32 NumEntries, pi_platform *Platforms,
// 1. sycl::platform equality issue; we always return the same pi_platform.
// 2. performance; we can save time by immediately return from cache.
//
// Note: The memory for "PiPlatformsCache" and "PiPlatformsCacheMutex" is
// intentionally leaked because the application may call into the SYCL
// runtime from a global destructor, and such a call could eventually
// access these variables. Therefore, there is no safe time when
// "PiPlatformsCache" and "PiPlatformsCacheMutex" could be deleted.
static auto PiPlatformsCache = new std::vector<pi_platform>;
static auto PiPlatformsCacheMutex = new std::mutex;
static bool PiPlatformCachePopulated = false;

std::lock_guard<std::mutex> Lock(*PiPlatformsCacheMutex);

const std::lock_guard<sycl::detail::SpinLock> Lock{*PiPlatformsCacheMutex};
if (!PiPlatformCachePopulated) {
const char *CommandListCacheSize =
std::getenv("SYCL_PI_LEVEL_ZERO_MAX_COMMAND_LIST_CACHE");
Expand Down Expand Up @@ -5349,4 +5353,18 @@ pi_result piPluginInit(pi_plugin *PluginInit) {
return PI_SUCCESS;
}

// SYCL RT calls this api to notify the end of plugin lifetime.
// It can include all the jobs to tear down resources before
// the plugin is unloaded from memory.
pi_result piTearDown(void *PluginParameter) {
// reclaim pi_platform objects here since we don't have piPlatformRelease.
for (pi_platform &Platform : *PiPlatformsCache) {
delete Platform;
}
delete PiPlatformsCache;
delete PiPlatformsCacheMutex;

return PI_SUCCESS;
}

} // extern "C"
6 changes: 6 additions & 0 deletions sycl/plugins/opencl/pi_opencl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,11 @@ pi_result piextProgramGetNativeHandle(pi_program program,
return piextGetNativeHandle(program, nativeHandle);
}

// This API is called by Sycl RT to notify the end of the plugin lifetime.
// TODO: add a global variable lifetime management code here (see
// pi_level_zero.cpp for reference) Currently this is just a NOOP.
pi_result piTearDown(void *PluginParameter) { return PI_SUCCESS; }

pi_result piPluginInit(pi_plugin *PluginInit) {
int CompareVersions = strcmp(PluginInit->PiVersion, SupportedVersion);
if (CompareVersions < 0) {
Expand Down Expand Up @@ -1297,6 +1302,7 @@ pi_result piPluginInit(pi_plugin *PluginInit) {

_PI_CL(piextKernelSetArgMemObj, piextKernelSetArgMemObj)
_PI_CL(piextKernelSetArgSampler, piextKernelSetArgSampler)
_PI_CL(piTearDown, piTearDown)

#undef _PI_CL

Expand Down
14 changes: 13 additions & 1 deletion sycl/source/detail/global_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include <CL/sycl/detail/device_filter.hpp>
#include <CL/sycl/detail/pi.hpp>
#include <CL/sycl/detail/spinlock.hpp>
#include <detail/global_handler.hpp>
#include <detail/platform_impl.hpp>
Expand Down Expand Up @@ -113,7 +114,18 @@ GlobalHandler::getDeviceFilterList(const std::string &InitValue) {
return *MDeviceFilterList;
}

void shutdown() { delete &GlobalHandler::instance(); }
void shutdown() {
for (plugin &Plugin : GlobalHandler::instance().getPlugins()) {
// PluginParameter is reserved for future use that can control
// some parameters in the plugin tear-down process.
// Currently, it is not used.
void *PluginParameter = nullptr;
Plugin.call_nocheck<PiApiKind::piTearDown>(PluginParameter);
Plugin.unload();
}

delete &GlobalHandler::instance();
}

#ifdef _WIN32
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
Expand Down
16 changes: 11 additions & 5 deletions sycl/source/detail/pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ void *loadPlugin(const std::string &PluginPath) {
return loadOsLibrary(PluginPath);
}

// Unload the given plugin by calling teh OS-specific library unloading call.
// \param Library OS-specific library handle created when loading.
int unloadPlugin(void *Library) { return unloadOsLibrary(Library); }

// Binds all the PI Interface APIs to Plugin Library Function Addresses.
// TODO: Remove the 'OclPtr' extension to PI_API.
// TODO: Change the functionality such that a single getOsLibraryFuncAddress
Expand Down Expand Up @@ -339,18 +343,20 @@ static void initializePlugins(vector_class<plugin> *Plugins) {
PluginNames[I].first.find("opencl") != std::string::npos) {
// Use the OpenCL plugin as the GlobalPlugin
GlobalPlugin =
std::make_shared<plugin>(PluginInformation, backend::opencl);
std::make_shared<plugin>(PluginInformation, backend::opencl, Library);
} else if (InteropBE == backend::cuda &&
PluginNames[I].first.find("cuda") != std::string::npos) {
// Use the CUDA plugin as the GlobalPlugin
GlobalPlugin = std::make_shared<plugin>(PluginInformation, backend::cuda);
GlobalPlugin =
std::make_shared<plugin>(PluginInformation, backend::cuda, Library);
} else if (InteropBE == backend::level_zero &&
PluginNames[I].first.find("level_zero") != std::string::npos) {
// Use the LEVEL_ZERO plugin as the GlobalPlugin
GlobalPlugin =
std::make_shared<plugin>(PluginInformation, backend::level_zero);
GlobalPlugin = std::make_shared<plugin>(PluginInformation,
backend::level_zero, Library);
}
Plugins->emplace_back(plugin(PluginInformation, PluginNames[I].second));
Plugins->emplace_back(
plugin(PluginInformation, PluginNames[I].second, Library));
if (trace(TraceLevel::PI_TRACE_BASIC))
std::cerr << "SYCL_PI_TRACE[basic]: "
<< "Plugin found and successfully loaded: "
Expand Down
8 changes: 6 additions & 2 deletions sycl/source/detail/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class plugin {
public:
plugin() = delete;

plugin(RT::PiPlugin Plugin, backend UseBackend)
: MPlugin(Plugin), MBackend(UseBackend),
plugin(RT::PiPlugin Plugin, backend UseBackend, void *LibraryHandle)
: MPlugin(Plugin), MBackend(UseBackend), MLibraryHandle(LibraryHandle),
TracingMutex(std::make_shared<std::mutex>()) {}

plugin &operator=(const plugin &) = default;
Expand Down Expand Up @@ -104,10 +104,14 @@ class plugin {
}

backend getBackend(void) const { return MBackend; }
void *getLibraryHandle() const { return MLibraryHandle; }
void *getLibraryHandle() { return MLibraryHandle; }
int unload() { return RT::unloadPlugin(MLibraryHandle); }

private:
RT::PiPlugin MPlugin;
backend MBackend;
void *MLibraryHandle; // the handle returned from dlopen
std::shared_ptr<std::mutex> TracingMutex;
}; // class plugin
} // namespace detail
Expand Down
2 changes: 2 additions & 0 deletions sycl/source/detail/posix_pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ void *loadOsLibrary(const std::string &PluginPath) {
return dlopen(PluginPath.c_str(), RTLD_NOW);
}

int unloadOsLibrary(void *Library) { return dlclose(Library); }

void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) {
return dlsym(Library, FunctionName.c_str());
}
Expand Down
6 changes: 5 additions & 1 deletion sycl/source/detail/windows_pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

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

#include <string>
#include <windows.h>
#include <winreg.h>
#include <string>

__SYCL_INLINE_NAMESPACE(cl) {
namespace sycl {
Expand All @@ -21,6 +21,10 @@ void *loadOsLibrary(const std::string &PluginPath) {
return (void *)LoadLibraryA(PluginPath.c_str());
}

int unloadOsLibrary(void *Library) {
return (int)FreeLibrary((HMODULE)Library);
}

void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) {
return reinterpret_cast<void *>(
GetProcAddress((HMODULE)Library, FunctionName.c_str()));
Expand Down
1 change: 1 addition & 0 deletions sycl/test/abi/pi_level_zero_symbol_check.dump
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ piSamplerCreate
piSamplerGetInfo
piSamplerRelease
piSamplerRetain
piTearDown
piclProgramCreateWithSource
piextContextCreateWithNativeHandle
piextContextGetNativeHandle
Expand Down
1 change: 1 addition & 0 deletions sycl/test/abi/pi_opencl_symbol_check.dump
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ piProgramCreateWithBinary
piProgramLink
piQueueCreate
piSamplerCreate
piTearDown
piclProgramCreateWithSource
piextContextCreateWithNativeHandle
piextContextGetNativeHandle
Expand Down
3 changes: 2 additions & 1 deletion sycl/unittests/helpers/PiMock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class PiMock {
// Copy the PiPlugin, thus untying our to-be mock platform from other
// platforms within the context. Reset our platform to use the new plugin.
auto NewPluginPtr = std::make_shared<detail::plugin>(
OriginalPiPlugin.getPiPlugin(), OriginalPiPlugin.getBackend());
OriginalPiPlugin.getPiPlugin(), OriginalPiPlugin.getBackend(),
OriginalPiPlugin.getLibraryHandle());
ImplPtr->setPlugin(NewPluginPtr);
// Extract the new PiPlugin instance by a non-const pointer,
// explicitly allowing modification
Expand Down