Skip to content

Commit f412bf7

Browse files
committed
Apply review feedback
Also change pool getter to avoid any dpctl4pybind11 including extensions from having their own thread pools
1 parent bba7b6e commit f412bf7

11 files changed

Lines changed: 129 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
* Added a number of `sycl::device` info queries to `dpctl.SyclDevice` [gh-2324](https://github.com/IntelPython/dpctl/pull/2324)
1212
* Added `sycl::info::context` queries `sycl_platform`, `atomic_memory_order_capabilities`, `atomic_fence_order_capabilities`, `atomic_memory_scope_capabilities`, and `atomic_fence_scope_capabilities` to `dpctl.SyclContext` [gh-2354](https://github.com/IntelPython/dpctl/pull/2354)
1313
* Added `create_kernel_bundle_from_sycl_source`, `is_sycl_source_compilation_available`, and `dpctl.SyclDevice.can_compile` for supporting the creation of `dpctl.SyclKernelBundle`s from SYCL source strings via DPC++ extension, as well as corresponding C-API functions to support it [gh-2206](https://github.com/IntelPython/dpctl/pull/2206)
14-
* Added `dpctl.keep_args_alive` free function, and `add_event` method to the order manager
14+
* Added `dpctl.keep_args_alive` free function, and `add_event` method to the order manager [gh-2359](https://github.com/IntelPython/dpctl/pull/2359)
15+
* Added `DPCTL_KEEP_ALIVE_POOL_SIZE` CMake option for setting the number of threads that keep objects alive during offload [gh-2359](https://github.com/IntelPython/dpctl/pull/2359)
1516

1617
### Deprecated
1718
* Deprecated `dpctl.SyclQueue._submit_keep_args_alive` in favor of `dpctl.keep_args_alive` [gh-2359](https://github.com/IntelPython/dpctl/pull/2359)

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ option(
4848
size of shared object with offloading sections"
4949
OFF
5050
)
51+
set(DPCTL_KEEP_ALIVE_POOL_SIZE
52+
"4"
53+
CACHE STRING
54+
"Number of threads in the pool that keeps Python objects used by \
55+
offloaded tasks alive until the tasks complete"
56+
)
57+
58+
if (NOT DPCTL_KEEP_ALIVE_POOL_SIZE MATCHES "^[1-9][0-9]*$")
59+
message(FATAL_ERROR
60+
"Invalid value for DPCTL_KEEP_ALIVE_POOL_SIZE: \
61+
\"${DPCTL_KEEP_ALIVE_POOL_SIZE}\". Expected a positive integer."
62+
)
63+
endif()
5164

5265
find_package(IntelSYCL REQUIRED PATHS ${CMAKE_SOURCE_DIR}/cmake NO_DEFAULT_PATH)
5366

docs/doc_sources/api_reference/dpctl/utils.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
referenced by a task alive until it completes, use
2222
:func:`dpctl.keep_args_alive`.
2323

24-
.. deprecated::
24+
.. deprecated:: 0.23.0
2525
``add_event_pair``, ``host_task_events`` and ``num_host_task_events``
2626
are deprecated. Tasks are no longer paired with a host task event,
2727
so ``add_event`` takes the computational event alone.

docs/doc_sources/beginners_guides/installation.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,24 @@ devices at the same time:
247247
248248
python scripts/build_locally.py --verbose --target-cuda --target-hip=gfx1030
249249
250+
Configuring the object management thread pool
251+
---------------------------------------------
252+
253+
To keep Python objects used by an offloaded task alive until tasks complete,
254+
:py:mod:`dpctl` maintains a pool of threads that wait on the task's events.
255+
The pool uses four threads by default. The size is fixed when ``dpctl`` is
256+
compiled and can be changed with the ``--keep-alive-pool-size`` argument:
257+
258+
.. code-block:: bash
259+
260+
python scripts/build_locally.py --verbose --keep-alive-pool-size=8
261+
262+
Alternatively, you use the ``DPCTL_KEEP_ALIVE_POOL_SIZE`` CMake option:
263+
264+
.. code-block:: bash
265+
266+
python scripts/build_locally.py --verbose --cmake-opts="-DDPCTL_KEEP_ALIVE_POOL_SIZE=8"
267+
250268
Running Examples and Tests
251269
==========================
252270

dpctl/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ target_include_directories(${_trgt} PRIVATE
205205
${CMAKE_CURRENT_SOURCE_DIR}
206206
${CMAKE_CURRENT_SOURCE_DIR}/apis/include
207207
)
208+
# _sycl_queue owns the pool, so only it needs to know the pool size
209+
target_compile_definitions(${_trgt} PRIVATE
210+
DPCTL_KEEP_ALIVE_POOL_SIZE=${DPCTL_KEEP_ALIVE_POOL_SIZE}
211+
)
208212
target_link_libraries(DpctlCAPI INTERFACE ${_trgt}_headers)
209213

210214
add_subdirectory(program)

dpctl/_async_dec_ref.hpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@
4242
#include "syclinterface/dpctl_data_types.h"
4343
#include "syclinterface/dpctl_sycl_type_casters.hpp"
4444

45+
/*!
46+
* @brief Address of the `KeepAlivePool`.
47+
*
48+
* Returns nullptr if the pool could not be created.
49+
*/
50+
void *keep_alive_pool_ptr()
51+
{
52+
try {
53+
return static_cast<void *>(
54+
&dpctl::detail::KeepAlivePool::local_instance());
55+
} catch (const std::exception &e) {
56+
return nullptr;
57+
}
58+
}
59+
4560
/*!
4661
* @brief Schedule DECREFs of `obj_array` for once `depERefs` have completed.
4762
*
@@ -64,17 +79,16 @@ void async_dec_ref(PyObject **obj_array,
6479
depends.push_back(*(unwrap<sycl::event>(depERefs[ev_id])));
6580
}
6681

67-
dpctl::detail::KeepAlivePool::get().submit(
82+
dpctl::detail::KeepAlivePool::local_instance().submit(
6883
std::move(depends),
6984
[obj_array_size, obj_vec = std::move(obj_vec)]() {
70-
const bool initialized = Py_IsInitialized();
7185
#if PY_VERSION_HEX < 0x30d0000
7286
const bool finalizing = _Py_IsFinalizing();
7387
#else
7488
const bool finalizing = Py_IsFinalizing();
7589
#endif
7690
// if the main thread has not finalized the interpreter yet
77-
if (initialized && !finalizing) {
91+
if (!finalizing) {
7892
PyGILState_STATE gstate;
7993
gstate = PyGILState_Ensure();
8094
for (size_t i = 0; i < obj_array_size; ++i) {

dpctl/_sycl_queue.pyx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ cdef extern from "_async_dec_ref.hpp":
102102
DPCTLSyclQueueRef, PyObject **,
103103
size_t, DPCTLSyclEventRef *, size_t, int *
104104
) nogil
105+
void *keep_alive_pool_ptr() nogil
105106

106107

107108
__all__ = [
@@ -1851,6 +1852,10 @@ cdef api SyclQueue SyclQueue_Make(DPCTLSyclQueueRef QRef):
18511852
cdef DPCTLSyclQueueRef copied_QRef = DPCTLQueue_Copy(QRef)
18521853
return SyclQueue._create(copied_QRef)
18531854

1855+
1856+
cdef api void *KeepAlivePool_Get() noexcept nogil:
1857+
return keep_alive_pool_ptr()
1858+
18541859
cdef class _WorkGroupMemory:
18551860
def __dealloc__(self):
18561861
if(self._mem_ref):

dpctl/apis/include/detail/keep_alive_pool.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838

3939
#include <sycl/sycl.hpp>
4040

41+
#ifndef DPCTL_KEEP_ALIVE_POOL_SIZE
42+
#define DPCTL_KEEP_ALIVE_POOL_SIZE 4
43+
#endif
44+
4145
namespace dpctl
4246
{
4347
namespace detail
@@ -49,9 +53,15 @@ class KeepAlivePool
4953
/*!
5054
* @brief Number of waiter threads.
5155
*/
52-
static constexpr std::size_t num_threads = 4;
56+
static constexpr std::size_t num_threads = DPCTL_KEEP_ALIVE_POOL_SIZE;
57+
58+
static_assert(num_threads > 0,
59+
"DPCTL_KEEP_ALIVE_POOL_SIZE must be greater than zero");
5360

54-
static KeepAlivePool &get()
61+
/*!
62+
* @brief The instance belonging to this shared object.
63+
*/
64+
static KeepAlivePool &local_instance()
5565
{
5666
// deliberately leaked: workers are detached and hold a bare `this`, so
5767
// the pool must outlive them

dpctl/apis/include/dpctl4pybind11.hpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ namespace dpctl
4747
namespace detail
4848
{
4949

50+
/*!
51+
* @brief Whether the interpreter can still be called into.
52+
*
53+
* Acquiring the GIL once finalization has begun does not return, so work
54+
* deferred to a thread must check this before touching Python.
55+
*/
56+
inline bool interpreter_is_live()
57+
{
58+
#if PY_VERSION_HEX < 0x30d0000
59+
return !_Py_IsFinalizing();
60+
#else
61+
return !Py_IsFinalizing();
62+
#endif
63+
}
64+
5065
class dpctl_capi
5166
{
5267
public:
@@ -174,15 +189,7 @@ class dpctl_capi
174189
{
175190
void operator()(py::object *p) const
176191
{
177-
const bool initialized = Py_IsInitialized();
178-
#if PY_VERSION_HEX < 0x30d0000
179-
const bool finalizing = _Py_IsFinalizing();
180-
#else
181-
const bool finalizing = Py_IsFinalizing();
182-
#endif
183-
const bool guard = initialized && !finalizing;
184-
185-
if (guard) {
192+
if (interpreter_is_live()) {
186193
delete p;
187194
}
188195
}
@@ -295,6 +302,25 @@ class dpctl_capi
295302
dpctl_capi &operator=(dpctl_capi &&) = default;
296303

297304
}; // struct dpctl_capi
305+
306+
/*!
307+
* @brief The `KeepAlivePool` singleton, owned by `dpctl._sycl_queue`.
308+
*/
309+
inline KeepAlivePool &get_keep_alive_pool()
310+
{
311+
static KeepAlivePool *pool = []() -> KeepAlivePool * {
312+
// get dpctl_capi to prevent nullptr return
313+
static_cast<void>(dpctl_capi::get());
314+
315+
return static_cast<KeepAlivePool *>(KeepAlivePool_Get());
316+
}();
317+
318+
if (!pool) {
319+
throw std::runtime_error("Could not create dpctl's keep-alive pool");
320+
}
321+
return *pool;
322+
}
323+
298324
} // namespace detail
299325
} // namespace dpctl
300326

@@ -828,14 +854,16 @@ sycl::event keep_args_alive(sycl::queue &q,
828854
}
829855

830856
if (n_usm_owners_held > 0 || n_objects_held > 0) {
831-
dpctl::detail::KeepAlivePool::get().submit(
857+
dpctl::detail::get_keep_alive_pool().submit(
832858
depends, [n_usm_owners_held, shp_usm = std::move(shp_usm),
833859
n_objects_held, shp_arr = std::move(shp_arr)]() mutable {
834860
for (std::size_t i = 0; i < n_usm_owners_held; ++i) {
835861
shp_usm[i].reset();
836862
}
837863

838-
if (n_objects_held > 0) {
864+
// if the main thread has not finalized the interpreter yet
865+
if (n_objects_held > 0 &&
866+
dpctl::detail::interpreter_is_live()) {
839867
py::gil_scoped_acquire acquire;
840868

841869
for (std::size_t i = 0; i < n_objects_held; ++i) {

scripts/_build_helper.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def make_cmake_args(
9393
glog: bool = False,
9494
verbose: bool = False,
9595
other_opts: str = None,
96+
keep_alive_pool_size: int = None,
9697
):
9798
args = [
9899
f"-DCMAKE_C_COMPILER:PATH={c_compiler}" if c_compiler else "",
@@ -101,6 +102,10 @@ def make_cmake_args(
101102
f"-DDPCTL_ENABLE_GLOG:BOOL={'ON' if glog else 'OFF'}",
102103
]
103104

105+
if keep_alive_pool_size is not None:
106+
args.append(
107+
f"-DDPCTL_KEEP_ALIVE_POOL_SIZE:STRING={keep_alive_pool_size}"
108+
)
104109
if verbose:
105110
args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
106111
if other_opts:

0 commit comments

Comments
 (0)