Repository navigation
Remove host_task object management
#2359
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,14 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |
| ## [dev] - XXX. XX, XXXX | ||
|
|
||
| ### Added | ||
| * Added `dpctl.SyclQueue.keep_args_alive` method, which keeps Python objects alive until the work on that queue using them completes [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
| * Added `add_event` and `add_cleanup_event` methods and `cleanup_events` and `num_cleanup_events` properties to the order manager, for tracking the events that gate the release of objects used by offloaded tasks [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
|
|
||
| ### Changed | ||
| * Object lifetime during offload is now managed by a list of releases deferred until the events that gate them have completed, rather than by a `host_task`. `dpctl` keeps one thread for looking over the list and running the releases that have come due, started by the first release deferred and again in the child of a `fork`, which takes the GIL to do so and lets a release call into the interpreter and free USM allocations [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
| * The event returned by `dpctl.SyclQueue.keep_args_alive`, `dpctl::utils::keep_args_alive` and `dpctl.SyclQueue._submit_keep_args_alive` is now for an empty kernel that gates the deferred release rather than for a `host_task` that performs it, so its completion means that the objects are no longer in use rather than that they have been released [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
|
|
||
| ### Deprecated | ||
|
|
||
| ### Removed | ||
|
|
||
| ### Fixed | ||
| * A USM allocation that fails now drops the references held for offloaded tasks that have completed, and is attempted once more if that released anything, so that memory only waiting to be given up is not reported as unavailable [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
|
|
||
| ### Maintenance | ||
|
|
||
|
|
@@ -63,6 +68,8 @@ This release extends the SYCL interface exposed by `dpctl` in several directions | |
| * Deprecated `dpctl.program.create_program_from_source` and `dpctl.program.create_program_from_spirv` in favor of `dpctl.compiler.create_kernel_bundle_from_source` and `dpctl.compiler.create_kernel_bundle_from_spirv`; the old functions are kept as aliases that emit a `DeprecationWarning` [gh-2291](https://github.com/IntelPython/dpctl/pull/2291), [gh-2317](https://github.com/IntelPython/dpctl/pull/2317) | ||
| * Deprecated `dpctl.program` submodule in favor of `dpctl.compiler`, which provides a better description of the purpose of the submodule in exposing DPC++ compilation-related functionality [gh-2317](https://github.com/IntelPython/dpctl/pull/2317) | ||
| * Deprecated `DPCTL_ENABLE_L0_PROGRAM_CREATION` CMake option in favor of `DPCTL_ENABLE_L0_KERNEL_BUNDLE_CREATION` [gh-2317](https://github.com/IntelPython/dpctl/pull/2317) | ||
| * Deprecated `dpctl.SyclQueue._submit_keep_args_alive` in favor of `dpctl.SyclQueue.keep_args_alive`, which it now calls and which behaves identically [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two entries ended up in the |
||
| * Deprecated the order manager's `add_event_pair`, `host_task_events` and `num_host_task_events`, as `host_task` is no longer used for managing object lifetimes, in favor of `add_event`, `add_cleanup_event`, `cleanup_events` and `num_cleanup_events` [gh-2359](https://github.com/IntelPython/dpctl/pull/2359) | ||
|
|
||
| ### Removed | ||
| * Removed unused `platform_typeid_lookup` from `dpctl4pybind11.hpp` [gh-2286](https://github.com/IntelPython/dpctl/pull/2286) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,3 +15,23 @@ | |
|
|
||
| Thread-local object mapping each :class:`dpctl.SyclQueue` to an order | ||
| manager, used to ensure sequential ordering of offloaded tasks. | ||
|
|
||
| Record submitted tasks with ``add_event`` and use ``submitted_events`` | ||
| as the dependency list of subsequent submissions. To keep Python objects | ||
| referenced by a task alive until it completes, use | ||
| :meth:`dpctl.SyclQueue.keep_args_alive`. | ||
|
|
||
| Record events that gate the release of objects used by a task with | ||
| ``add_cleanup_event``, and find them in ``cleanup_events``. They are | ||
| waited on, but never become dependencies of later tasks. | ||
|
|
||
| Waiting with ``wait`` also drops the references that | ||
| :meth:`dpctl.SyclQueue.keep_args_alive` took for tasks that have since | ||
| completed, which ``dpctl`` otherwise does on a thread of its own. | ||
|
|
||
| .. deprecated:: 0.23.0 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ``add_event_pair``, ``host_task_events`` and ``num_host_task_events`` | ||
| are deprecated. Tasks are no longer paired with a host task event, so | ||
| ``add_event`` takes the computational event alone, and cleanup is | ||
| tracked by ``add_cleanup_event``, ``cleanup_events`` and | ||
| ``num_cleanup_events``. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| //===--- _async_dec_ref.hpp - Implements async DECREF ---------------------===// | ||
| // | ||
| // Data Parallel Control (dpctl) | ||
| // | ||
| // Copyright 2026 Intel Corporation | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file implements a utility function to decrement reference counts for a | ||
| /// given array of Python objects once a given array of sycl events has | ||
| /// completed. | ||
| /// | ||
| /// N.B.: The reference counts are dropped by the thread `dpctl` keeps for | ||
| /// running deferred releases, or by a thread that drains them. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #pragma once | ||
| #include <memory> | ||
| #include <stddef.h> | ||
| #include <stdexcept> | ||
| #include <sycl/sycl.hpp> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include "Python.h" | ||
|
|
||
| #include "_deferred_release_watcher.hpp" | ||
| #include "syclinterface/dpctl_data_types.h" | ||
| #include "syclinterface/dpctl_sycl_type_casters.hpp" | ||
|
|
||
| namespace | ||
| { | ||
|
|
||
| /*! | ||
| * @brief Copy the events behind `nDepERefs` references into a vector. | ||
| */ | ||
| std::vector<sycl::event> unwrap_events(DPCTLSyclEventRef *depERefs, | ||
| size_t nDepERefs) | ||
| { | ||
| using dpctl::syclinterface::unwrap; | ||
|
|
||
| std::vector<sycl::event> depends; | ||
| depends.reserve(nDepERefs); | ||
| for (size_t ev_id = 0; ev_id < nDepERefs; ++ev_id) { | ||
| depends.push_back(*(unwrap<sycl::event>(depERefs[ev_id]))); | ||
| } | ||
|
|
||
| return depends; | ||
| } | ||
|
|
||
| /*! | ||
| * @brief Schedule DECREFs of `obj_vec` for once `depends` have completed. | ||
| */ | ||
| void submit_dec_ref(std::vector<PyObject *> obj_vec, | ||
| std::vector<sycl::event> depends) | ||
| { | ||
| dpctl::detail::local_deferred_releases().defer( | ||
| std::move(depends), [obj_vec = std::move(obj_vec)]() { | ||
| for (PyObject *obj : obj_vec) { | ||
| Py_DECREF(obj); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| /*! | ||
| * @brief Schedule DECREFs of `obj_array` for once `obj_array` is out of use. | ||
| * | ||
| * An empty kernel is submitted to `QRef` after `depERefs`, and the DECREFs wait | ||
| * for it. On an in-order queue submission also orders it after the work already | ||
| * submitted there, so the objects are covered regardless of providing dependent | ||
| * events. `QRef` should be the queue running the work that uses the objects. | ||
| * An event for the kernel is returned. | ||
| * | ||
| * Sets `*status` to 0 on success and 1 if scheduling threw, and returns nullptr | ||
| * on failure. A failed call has scheduled nothing, so the caller still owns the | ||
| * references it took. | ||
| */ | ||
| inline DPCTLSyclEventRef async_dec_ref(DPCTLSyclQueueRef QRef, | ||
| PyObject **obj_array, | ||
| size_t obj_array_size, | ||
| DPCTLSyclEventRef *depERefs, | ||
| size_t nDepERefs, | ||
| int *status) | ||
| { | ||
| using dpctl::syclinterface::unwrap; | ||
| using dpctl::syclinterface::wrap; | ||
|
|
||
| try { | ||
| sycl::queue *q = unwrap<sycl::queue>(QRef); | ||
| if (!q) { | ||
| throw std::invalid_argument("A queue is required"); | ||
| } | ||
|
|
||
| std::vector<sycl::event> depends = unwrap_events(depERefs, nDepERefs); | ||
| std::vector<PyObject *> obj_vec(obj_array, obj_array + obj_array_size); | ||
|
|
||
| static constexpr int result_ok = 0; | ||
|
|
||
| const sycl::event marker = | ||
| dpctl::detail::submit_keep_alive_marker(*q, depends); | ||
|
|
||
| // allocated before scheduling, as failing afterwards could not be | ||
| // reported: the caller would drop a reference the scheduled DECREFs own | ||
| std::unique_ptr<sycl::event> e_ptr(new sycl::event(marker)); | ||
|
|
||
| submit_dec_ref(std::move(obj_vec), {marker}); | ||
|
|
||
| *status = result_ok; | ||
|
|
||
| return wrap<sycl::event>(e_ptr.release()); | ||
| } catch (...) { | ||
| // no exception may escape into the calling Cython code | ||
| static constexpr int result_exception = 1; | ||
| *status = result_exception; | ||
| return nullptr; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the changelog doesn't mention the new installed header
dpctl/include/detail/deferred_releases.hpp.