diff --git a/sycl/doc/EnvironmentVariables.md b/sycl/doc/EnvironmentVariables.md index 43af7fc852f00..ae21006aa6093 100644 --- a/sycl/doc/EnvironmentVariables.md +++ b/sycl/doc/EnvironmentVariables.md @@ -47,6 +47,7 @@ subject to change. Do not rely on these variables in production code. | `SYCL_CACHE_THRESHOLD` | Positive integer | Cache eviction threshold in days (default value is 7 for 1 week). Set to 0 for disabling time-based cache eviction. | | `SYCL_CACHE_MIN_DEVICE_IMAGE_SIZE` | Positive integer | Minimum size of device code image in bytes which is reasonable to cache on disk because disk access operation may take more time than do JIT compilation for it. Default value is 0 to cache all images. | | `SYCL_CACHE_MAX_DEVICE_IMAGE_SIZE` | Positive integer | Maximum size of device image in bytes which is cached. Too big kernels may overload disk too fast. Default value is 1 GB. | +| `SYCL_ENABLE_DEFAULT_CONTEXTS` | '1' or '0' | Enable ('1') or disable ('0') creation of default platform contexts in SYCL runtime. The default context for each platform contains all devices in the platform. Refer to [Platform Default Contexts](extensions/PlatformContext/PlatformContext.adoc) extension to learn more. Enabled by default on Linux and disabled on Windows. | `(*) Note: Any means this environment variable is effective when set to any non-null value.` diff --git a/sycl/doc/extensions/PlatformContext/PlatformContext.adoc b/sycl/doc/extensions/PlatformContext/PlatformContext.adoc new file mode 100644 index 0000000000000..6b9e764f1b446 --- /dev/null +++ b/sycl/doc/extensions/PlatformContext/PlatformContext.adoc @@ -0,0 +1,34 @@ += SYCL(TM) Proposals: Platform Default Contexts +James Brodman +v0.1 +:source-highlighter: pygments +:icons: font +:y: icon:check[role="green"] +:n: icon:times[role="red"] + +== Platform Default Contexts + +This extension adds the notion of a default SYCL context per SYCL platform. The default context for each platform contains all devices in the platform. + +The platform class gains one new method: + +[cols="^60a,40"] +|=== +| Member Function | Description + +| +[source,c++] +---- +context ext_oneapi_get_default_context() +---- + +| Returns the current default context for this `platform` + +|=== + +This extension also modifies the behavior of `queue` constructors. Queues will no longer create a new `context` upon construction. Instead, they will use the default context from the device's platform. + +== Feature Test Macro + +This extension defines the macro `SYCL_EXT_ONEAPI_DEFAULT_CONTEXT` to `1` to indicate that it is enabled. + diff --git a/sycl/doc/extensions/README.md b/sycl/doc/extensions/README.md index ae345a98920e2..c55e2ecc58be6 100755 --- a/sycl/doc/extensions/README.md +++ b/sycl/doc/extensions/README.md @@ -37,6 +37,7 @@ DPC++ extensions status: | [Use Pinned Memory Property](UsePinnedMemoryProperty/UsePinnedMemoryPropery.adoc) | Supported | | | [Level-Zero backend specification](LevelZeroBackend/LevelZeroBackend.md) | Supported | | | [ITT annotations support](ITTAnnotations/ITTAnnotations.rst) | Supported | | +| [Platform Context](PlatformContext/PlatformContext.adoc) | Proposal | | | [SYCL_EXT_ONEAPI_DEVICE_IF](DeviceIf/device_if.asciidoc) | Proposal | | | [SYCL_INTEL_group_sort](GroupAlgorithms/SYCL_INTEL_group_sort.asciidoc) | Proposal | | | [Invoke SIMD](InvokeSIMD/InvokeSIMD.asciidoc) | Proposal | | diff --git a/sycl/include/CL/sycl/platform.hpp b/sycl/include/CL/sycl/platform.hpp index 4b8fb726fe096..14ec6dc60be1a 100644 --- a/sycl/include/CL/sycl/platform.hpp +++ b/sycl/include/CL/sycl/platform.hpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #pragma once + #include +#include #include #include #include @@ -25,6 +27,9 @@ namespace detail { class platform_impl; } +// Feature test macro for Default Context +#define SYCL_EXT_ONEAPI_DEFAULT_CONTEXT 1 + /// Encapsulates a SYCL platform on which kernels may be executed. /// /// \ingroup sycl_api @@ -131,6 +136,11 @@ class __SYCL_EXPORT platform { /// given feature. bool has(aspect Aspect) const; + /// Return this platform's default context + /// + /// \return the default context + context ext_oneapi_get_default_context() const; + private: pi_native_handle getNative() const; diff --git a/sycl/plugins/cuda/pi_cuda.cpp b/sycl/plugins/cuda/pi_cuda.cpp index 31b7266323559..67836cc13a263 100644 --- a/sycl/plugins/cuda/pi_cuda.cpp +++ b/sycl/plugins/cuda/pi_cuda.cpp @@ -111,7 +111,7 @@ pi_result forLatestEvents(const pi_event *event_wait_list, /// pi_result check_error(CUresult result, const char *function, int line, const char *file) { - if (result == CUDA_SUCCESS) { + if (result == CUDA_SUCCESS || result == CUDA_ERROR_DEINITIALIZED) { return PI_SUCCESS; } diff --git a/sycl/source/detail/config.def b/sycl/source/detail/config.def index 86ed251967e3e..995ba885b46b2 100644 --- a/sycl/source/detail/config.def +++ b/sycl/source/detail/config.def @@ -38,3 +38,4 @@ CONFIG(SYCL_OVERRIDE_PI_OPENCL, 1024, __SYCL_OVERRIDE_PI_OPENCL) CONFIG(SYCL_OVERRIDE_PI_LEVEL_ZERO, 1024, __SYCL_OVERRIDE_PI_LEVEL_ZERO) CONFIG(SYCL_OVERRIDE_PI_CUDA, 1024, __SYCL_OVERRIDE_PI_CUDA) CONFIG(SYCL_OVERRIDE_PI_ROCM, 1024, __SYCL_OVERRIDE_PI_ROCM) +CONFIG(SYCL_ENABLE_DEFAULT_CONTEXTS, 1, __SYCL_ENABLE_DEFAULT_CONTEXTS) diff --git a/sycl/source/detail/config.hpp b/sycl/source/detail/config.hpp index c81a246ad9296..d8e6396adfdd3 100644 --- a/sycl/source/detail/config.hpp +++ b/sycl/source/detail/config.hpp @@ -282,6 +282,38 @@ template <> class SYCLConfig { } }; +template <> class SYCLConfig { + using BaseT = SYCLConfigBase; + +public: + static bool get() { +#ifdef WIN32 + constexpr bool DefaultValue = false; +#else + constexpr bool DefaultValue = true; +#endif + + const char *ValStr = getCachedValue(); + + if (!ValStr) + return DefaultValue; + + return ValStr[0] == '1'; + } + + static void reset() { (void)getCachedValue(/*ResetCache=*/true); } + + static const char *getName() { return BaseT::MConfigName; } + +private: + static const char *getCachedValue(bool ResetCache = false) { + static const char *ValStr = BaseT::getRawValue(); + if (ResetCache) + ValStr = BaseT::getRawValue(); + return ValStr; + } +}; + } // namespace detail } // namespace sycl } // __SYCL_INLINE_NAMESPACE(cl) diff --git a/sycl/source/detail/global_handler.cpp b/sycl/source/detail/global_handler.cpp index 692a0044157c2..e889368aa71ab 100644 --- a/sycl/source/detail/global_handler.cpp +++ b/sycl/source/detail/global_handler.cpp @@ -51,6 +51,15 @@ ProgramManager &GlobalHandler::getProgramManager() { return getOrCreate(MProgramManager); } +std::unordered_map & +GlobalHandler::getPlatformToDefaultContextCache() { + return getOrCreate(MPlatformToDefaultContextCache); +} + +std::mutex &GlobalHandler::getPlatformToDefaultContextCacheMutex() { + return getOrCreate(MPlatformToDefaultContextCacheMutex); +} + Sync &GlobalHandler::getSync() { return getOrCreate(MSync); } std::vector &GlobalHandler::getPlatformCache() { @@ -84,6 +93,16 @@ void shutdown() { // First, release resources, that may access plugins. GlobalHandler::instance().MScheduler.Inst.reset(nullptr); GlobalHandler::instance().MProgramManager.Inst.reset(nullptr); +#ifndef _WIN32 + GlobalHandler::instance().MPlatformToDefaultContextCache.Inst.reset(nullptr); +#else + // Windows does not maintain dependencies between dynamically loaded libraries + // and can unload SYCL runtime dependencies before sycl.dll's DllMain has + // finished. To avoid calls to nowhere, intentionally leak platform to device + // cache. This will prevent destructors from being called, thus no PI cleanup + // routines will be called in the end. + GlobalHandler::instance().MPlatformToDefaultContextCache.Inst.release(); +#endif GlobalHandler::instance().MPlatformCache.Inst.reset(nullptr); // Call to GlobalHandler::instance().getPlugins() initializes plugins. If diff --git a/sycl/source/detail/global_handler.hpp b/sycl/source/detail/global_handler.hpp index fe785f96c50d9..c5cfb7bdbfc46 100644 --- a/sycl/source/detail/global_handler.hpp +++ b/sycl/source/detail/global_handler.hpp @@ -12,11 +12,13 @@ #include #include +#include __SYCL_INLINE_NAMESPACE(cl) { namespace sycl { namespace detail { class platform_impl; +class context_impl; class Scheduler; class ProgramManager; class Sync; @@ -25,6 +27,7 @@ class device_filter_list; class XPTIRegistry; using PlatformImplPtr = std::shared_ptr; +using ContextImplPtr = std::shared_ptr; /// Wrapper class for global data structures with non-trivial destructors. /// @@ -53,6 +56,11 @@ class GlobalHandler { ProgramManager &getProgramManager(); Sync &getSync(); std::vector &getPlatformCache(); + + std::unordered_map & + getPlatformToDefaultContextCache(); + + std::mutex &getPlatformToDefaultContextCacheMutex(); std::mutex &getPlatformMapMutex(); std::mutex &getFilterMutex(); std::vector &getPlugins(); @@ -80,6 +88,9 @@ class GlobalHandler { InstWithLock MProgramManager; InstWithLock MSync; InstWithLock> MPlatformCache; + InstWithLock> + MPlatformToDefaultContextCache; + InstWithLock MPlatformToDefaultContextCacheMutex; InstWithLock MPlatformMapMutex; InstWithLock MFilterMutex; InstWithLock> MPlugins; diff --git a/sycl/source/detail/platform_impl.cpp b/sycl/source/detail/platform_impl.cpp index 42a639dae7835..de3f489217657 100644 --- a/sycl/source/detail/platform_impl.cpp +++ b/sycl/source/detail/platform_impl.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include diff --git a/sycl/source/detail/platform_impl.hpp b/sycl/source/detail/platform_impl.hpp index 37c5f03aa0a64..bb2343f57d653 100644 --- a/sycl/source/detail/platform_impl.hpp +++ b/sycl/source/detail/platform_impl.hpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #pragma once + #include #include #include diff --git a/sycl/source/detail/queue_impl.hpp b/sycl/source/detail/queue_impl.hpp index 4b5fa9f494222..1f1b678885b46 100644 --- a/sycl/source/detail/queue_impl.hpp +++ b/sycl/source/detail/queue_impl.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,22 @@ enum QueueOrder { Ordered, OOO }; class queue_impl { public: + // \return a default context for the platform if it includes the device + // passed and default contexts are enabled, a new context otherwise. + static ContextImplPtr getDefaultOrNew(const DeviceImplPtr &Device) { + if (!SYCLConfig::get()) + return detail::getSyclObjImpl( + context{createSyclObjFromImpl(Device), {}, {}}); + + ContextImplPtr DefaultContext = detail::getSyclObjImpl( + Device->get_platform().ext_oneapi_get_default_context()); + + if (DefaultContext->hasDevice(Device)) + return DefaultContext; + + return detail::getSyclObjImpl( + context{createSyclObjFromImpl(Device), {}, {}}); + } /// Constructs a SYCL queue from a device using an async_handler and /// property_list provided. /// @@ -59,14 +76,7 @@ class queue_impl { /// \param PropList is a list of properties to use for queue construction. queue_impl(const DeviceImplPtr &Device, const async_handler &AsyncHandler, const property_list &PropList) - : queue_impl(Device, - detail::getSyclObjImpl( - context(createSyclObjFromImpl(Device), {}, - (DefaultContextType == CUDAContextT::primary) - ? property_list{property::context::cuda:: - use_primary_context()} - : property_list{})), - AsyncHandler, PropList){}; + : queue_impl(Device, getDefaultOrNew(Device), AsyncHandler, PropList){}; /// Constructs a SYCL queue with an async_handler and property_list provided /// form a device and a context. diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 56ec1aec109c3..cbc95019c59f2 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -420,8 +420,10 @@ void Scheduler::deallocateStreamBuffers(stream_impl *Impl) { Scheduler::Scheduler() { sycl::device HostDevice; + sycl::context HostContext{HostDevice}; DefaultHostQueue = QueueImplPtr( - new queue_impl(detail::getSyclObjImpl(HostDevice), /*AsyncHandler=*/{}, + new queue_impl(detail::getSyclObjImpl(HostDevice), + detail::getSyclObjImpl(HostContext), /*AsyncHandler=*/{}, /*PropList=*/{})); } diff --git a/sycl/source/platform.cpp b/sycl/source/platform.cpp index b38f28960030e..a78bb18798ead 100644 --- a/sycl/source/platform.cpp +++ b/sycl/source/platform.cpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include __SYCL_INLINE_NAMESPACE(cl) { @@ -65,5 +67,26 @@ bool platform::has(aspect Aspect) const { return impl->has(Aspect); } #undef __SYCL_PARAM_TRAITS_SPEC +context platform::ext_oneapi_get_default_context() const { + if (!detail::SYCLConfig::get()) + throw std::runtime_error("SYCL default contexts are not enabled"); + + // Keeping the default context for platforms in the global cache to avoid + // shared_ptr based circular dependency between platform and context classes + std::unordered_map + &PlatformToDefaultContextCache = + detail::GlobalHandler::instance().getPlatformToDefaultContextCache(); + + std::lock_guard Lock{detail::GlobalHandler::instance() + .getPlatformToDefaultContextCacheMutex()}; + + auto It = PlatformToDefaultContextCache.find(impl); + if (PlatformToDefaultContextCache.end() == It) + std::tie(It, std::ignore) = PlatformToDefaultContextCache.insert( + {impl, detail::getSyclObjImpl(context{get_devices()})}); + + return detail::createSyclObjFromImpl(It->second); +} + } // namespace sycl } // __SYCL_INLINE_NAMESPACE(cl) diff --git a/sycl/test/abi/sycl_symbols_linux.dump b/sycl/test/abi/sycl_symbols_linux.dump index 48df1d6dd9ff1..33781ad6749d6 100644 --- a/sycl/test/abi/sycl_symbols_linux.dump +++ b/sycl/test/abi/sycl_symbols_linux.dump @@ -4325,6 +4325,7 @@ _ZNK2cl4sycl7samplerneERKS1_ _ZNK2cl4sycl8platform11get_backendEv _ZNK2cl4sycl8platform11get_devicesENS0_4info11device_typeE _ZNK2cl4sycl8platform13has_extensionERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE +_ZNK2cl4sycl8platform30ext_oneapi_get_default_contextEv _ZNK2cl4sycl8platform3getEv _ZNK2cl4sycl8platform3hasENS0_6aspectE _ZNK2cl4sycl8platform7is_hostEv diff --git a/sycl/test/abi/sycl_symbols_windows.dump b/sycl/test/abi/sycl_symbols_windows.dump index dc854bf1395eb..73e700820f201 100644 --- a/sycl/test/abi/sycl_symbols_windows.dump +++ b/sycl/test/abi/sycl_symbols_windows.dump @@ -1741,6 +1741,7 @@ ?extractArgsAndReqs@handler@sycl@cl@@AEAAXXZ ?extractArgsAndReqsFromLambda@handler@sycl@cl@@AEAAXPEAD_KPEBUkernel_param_desc_t@detail@23@@Z ?extractArgsAndReqsFromLambda@handler@sycl@cl@@AEAAXPEAD_KPEBUkernel_param_desc_t@detail@23@_N@Z +?ext_oneapi_get_default_context@platform@sycl@cl@@QEBA?AVcontext@23@XZ ?fabs@__host_std@cl@@YA?AV?$vec@M$00@sycl@2@V342@@Z ?fabs@__host_std@cl@@YA?AV?$vec@M$01@sycl@2@V342@@Z ?fabs@__host_std@cl@@YA?AV?$vec@M$02@sycl@2@V342@@Z diff --git a/sycl/unittests/CMakeLists.txt b/sycl/unittests/CMakeLists.txt index 61aa3fae3e19c..d109aa526dc80 100644 --- a/sycl/unittests/CMakeLists.txt +++ b/sycl/unittests/CMakeLists.txt @@ -21,3 +21,4 @@ add_subdirectory(SYCL2020) add_subdirectory(thread_safety) add_subdirectory(program_manager) add_subdirectory(assert) +add_subdirectory(Extensions) diff --git a/sycl/unittests/Extensions/CMakeLists.txt b/sycl/unittests/Extensions/CMakeLists.txt new file mode 100644 index 0000000000000..934e5ac53c784 --- /dev/null +++ b/sycl/unittests/Extensions/CMakeLists.txt @@ -0,0 +1,8 @@ +set(CMAKE_CXX_EXTENSIONS OFF) + +# Enable exception handling for these unit tests +set(LLVM_REQUIRES_EH 1) +add_sycl_unittest(ExtensionsTests OBJECT + DefaultContext.cpp +) + diff --git a/sycl/unittests/Extensions/DefaultContext.cpp b/sycl/unittests/Extensions/DefaultContext.cpp new file mode 100644 index 0000000000000..9552b2e4b47dc --- /dev/null +++ b/sycl/unittests/Extensions/DefaultContext.cpp @@ -0,0 +1,67 @@ +//==--------------------- DefaultContext.cpp -------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include +#include +#include + +#include + +// Same as defined in config.def +inline constexpr auto EnableDefaultContextsName = + "SYCL_ENABLE_DEFAULT_CONTEXTS"; + +TEST(DefaultContextTest, DefaultContextTest) { + using namespace sycl::detail; + using namespace sycl::unittest; + ScopedEnvVar var(EnableDefaultContextsName, "1", + SYCLConfig::reset); + + sycl::platform Plt1{sycl::default_selector()}; + sycl::unittest::PiMock Mock1{Plt1}; + setupDefaultMockAPIs(Mock1); + + sycl::platform Plt2{sycl::default_selector()}; + sycl::unittest::PiMock Mock2{Plt2}; + setupDefaultMockAPIs(Mock2); + + const sycl::device Dev1 = Plt1.get_devices()[0]; + const sycl::device Dev2 = Plt2.get_devices()[0]; + + sycl::queue Queue1{Dev1}; + sycl::queue Queue2{Dev2}; + + ASSERT_EQ(Queue1.get_context(), Queue2.get_context()); + + ASSERT_EQ(Dev1.get_platform().ext_oneapi_get_default_context(), + Dev2.get_platform().ext_oneapi_get_default_context()); +} + +TEST(DefaultContextTest, DefaultContextCanBeDisabled) { + using namespace sycl::detail; + using namespace sycl::unittest; + ScopedEnvVar var(EnableDefaultContextsName, "0", + SYCLConfig::reset); + + sycl::platform Plt{sycl::default_selector()}; + sycl::unittest::PiMock Mock{Plt}; + setupDefaultMockAPIs(Mock); + + bool catchException = false; + try { + (void)Plt.ext_oneapi_get_default_context(); + } catch (const std::runtime_error &) { + catchException = true; + } + + ASSERT_TRUE(catchException) + << "ext_oneapi_get_default_context did not throw and exception"; +} diff --git a/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp b/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp index 343cb522faa6d..77835923dbd0f 100644 --- a/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp +++ b/sycl/unittests/SYCL2020/GetNativeOpenCL.cpp @@ -22,7 +22,8 @@ using namespace cl::sycl; -int TestCounter; +int TestCounter = 0; +int DeviceRetainCounter = 0; static pi_result redefinedContextRetain(pi_context c) { ++TestCounter; @@ -36,6 +37,7 @@ static pi_result redefinedQueueRetain(pi_queue c) { static pi_result redefinedDeviceRetain(pi_device c) { ++TestCounter; + ++DeviceRetainCounter; return PI_SUCCESS; } @@ -111,7 +113,8 @@ TEST(GetNative, GetNativeHandle) { get_native(Device); get_native(Event); - // When creating a context, the piDeviceRetain is called so here is the 6 - // retain calls - ASSERT_EQ(TestCounter, 6) << "Not all the retain methods was called"; + // Depending on global caches state, piDeviceRetain is called either once or + // twice, so there'll be 5 or 6 calls. + ASSERT_EQ(TestCounter, 5 + DeviceRetainCounter - 1) + << "Not all the retain methods were called"; } diff --git a/sycl/unittests/helpers/ScopedEnvVar.hpp b/sycl/unittests/helpers/ScopedEnvVar.hpp new file mode 100644 index 0000000000000..a078eeab2f537 --- /dev/null +++ b/sycl/unittests/helpers/ScopedEnvVar.hpp @@ -0,0 +1,59 @@ +//==----------------------- ScopedEnvVar.hpp -------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include + +__SYCL_INLINE_NAMESPACE(cl) { +namespace sycl::unittest { +inline void set_env(const char *Name, const char *Value) { +#ifdef _WIN32 + (void)_putenv_s(Name, Value); +#else + (void)setenv(Name, Value, /*overwrite*/ 1); +#endif +} + +inline void unset_env(const char *Name) { +#ifdef _WIN32 + (void)_putenv_s(Name, ""); +#else + unsetenv(Name); +#endif +} + +class ScopedEnvVar { +public: + ScopedEnvVar(const char *Name, const char *Value, + std::function ConfigReset) + : MName(Name), MConfigReset(ConfigReset) { + if (getenv(Name)) { + MOriginalValue = std::string(getenv(Name)); + } + set_env(Name, Value); + MConfigReset(); + } + + ~ScopedEnvVar() { + if (!MOriginalValue.empty()) { + set_env(MName, MOriginalValue.c_str()); + } else { + unset_env(MName); + } + MConfigReset(); + } + +private: + std::string MOriginalValue; + const char *MName; + std::function MConfigReset; +}; +} // namespace sycl::unittest +} // __SYCL_INLINE_NAMESPACE(cl)