Skip to content

Commit 96cd26e

Browse files
Create sequential order manager
The manager stores a list of events for tasks submitted so far, as well as list of events for host_tasks submitted so far. Every opperation of the manager prunes lists of completed events. `dpctl.utils.SequentialOrderManager` class instance is Python API to work with this class. Every offloading operation calls `.submitted_events` property to get dependent events, and adds computational event and host_task event to the manager using `.add_to_both_events(ht_ev, comp_ev)` method. The destructor of manager synchronizes on outstanding events.
1 parent 08b6dd0 commit 96cd26e

File tree

4 files changed

+310
-0
lines changed

4 files changed

+310
-0
lines changed

dpctl/utils/CMakeLists.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,37 @@ if(_dpctl_sycl_targets)
3636
endif()
3737
target_link_libraries(${python_module_name} PRIVATE DpctlCAPI)
3838
install(TARGETS ${python_module_name} DESTINATION "dpctl/utils")
39+
40+
41+
set(python_module_name _seq_order_keeper)
42+
set(_module_src ${CMAKE_CURRENT_SOURCE_DIR}/src/order_keeper.cpp)
43+
pybind11_add_module(${python_module_name} MODULE
44+
${_module_src}
45+
)
46+
target_include_directories(${python_module_name} PRIVATE ${CUMAKE_CURRENT_SOURCE_DIR}/src)
47+
add_sycl_to_target(TARGET ${python_module_name} SOURCES ${_module_src})
48+
if(DPCTL_GENERATE_COVERAGE)
49+
if(DPCTL_GENERATE_COVERAGE_FOR_PYBIND11_EXTENSIONS)
50+
target_compile_options(${python_module_name}
51+
PRIVATE -fprofile-instr-generate -fcoverage-mapping
52+
)
53+
endif()
54+
target_link_options(${python_module_name}
55+
PRIVATE -fprofile-instr-generate -fcoverage-mapping
56+
)
57+
endif()
58+
if(_dpctl_sycl_targets)
59+
# make fat binary
60+
target_compile_options(
61+
${python_module_name}
62+
PRIVATE
63+
-fsycl-targets=${_dpctl_sycl_targets}
64+
)
65+
target_link_options(
66+
${python_module_name}
67+
PRIVATE
68+
-fsycl-targets=${_dpctl_sycl_targets}
69+
)
70+
endif()
71+
target_link_libraries(${python_module_name} PRIVATE DpctlCAPI)
72+
install(TARGETS ${python_module_name} DESTINATION "dpctl/utils")

dpctl/utils/__init__.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
A collection of utility functions.
1919
"""
2020

21+
from contextvars import ContextVar
22+
2123
from .._sycl_device import SyclDevice
24+
from .._sycl_event import SyclEvent
2225
from ._compute_follows_data import (
2326
ExecutionPlacementError,
2427
get_coerced_usm_type,
@@ -39,6 +42,7 @@
3942
intel_device_info_memory_clock_rate,
4043
)
4144
from ._onetrace_context import onetrace_enabled
45+
from ._seq_order_keeper import _OrderManager
4246

4347

4448
def intel_device_info(dev, /):
@@ -119,11 +123,73 @@ def intel_device_info(dev, /):
119123
return dict()
120124

121125

126+
class _SequentialOrderManager:
127+
"""
128+
Class to orchestrate default sequential order
129+
of the tasks offloaded from Python.
130+
"""
131+
132+
def __init__(self):
133+
self._state = ContextVar("_seq_order_keeper", default=_OrderManager(16))
134+
135+
def __dealloc__(self):
136+
_local = self._state.get()
137+
SyclEvent.wait_for(_local.get_submitted_events())
138+
SyclEvent.wait_for(_local.get_host_task_events())
139+
140+
def __repr__(self):
141+
return "<dpctl.utils.SequentialOrderManager>"
142+
143+
def __str__(self):
144+
return "<dpctl.utils.SequentialOrderManager>"
145+
146+
def add_event_pair(self, host_task_ev, comp_ev):
147+
_local = self._state.get()
148+
if isinstance(host_task_ev, SyclEvent) and isinstance(
149+
comp_ev, SyclEvent
150+
):
151+
_local.add_to_both_events(host_task_ev, comp_ev)
152+
else:
153+
if not isinstance(host_task_ev, (list, tuple)):
154+
host_task_ev = (host_task_ev,)
155+
if not isinstance(comp_ev, (list, tuple)):
156+
comp_ev = (comp_ev,)
157+
_local.add_vector_to_both_events(host_task_ev, comp_ev)
158+
159+
@property
160+
def num_host_task_events(self):
161+
_local = self._state.get()
162+
return _local.get_num_host_task_events()
163+
164+
@property
165+
def num_submitted_events(self):
166+
_local = self._state.get()
167+
return _local.get_num_submitted_events()
168+
169+
@property
170+
def host_task_events(self):
171+
_local = self._state.get()
172+
return _local.get_host_task_events()
173+
174+
@property
175+
def submitted_events(self):
176+
_local = self._state.get()
177+
return _local.get_submitted_events()
178+
179+
def wait(self):
180+
_local = self._state.get()
181+
return _local.wait()
182+
183+
184+
SequentialOrderManager = _SequentialOrderManager()
185+
SequentialOrderManager.__name__ = "SequentialOrderManager"
186+
122187
__all__ = [
123188
"get_execution_queue",
124189
"get_coerced_usm_type",
125190
"validate_usm_type",
126191
"onetrace_enabled",
127192
"intel_device_info",
128193
"ExecutionPlacementError",
194+
"SequentialOrderManager",
129195
]

dpctl/utils/src/order_keeper.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "dpctl4pybind11.hpp"
2+
#include <pybind11/pybind11.h>
3+
#include <pybind11/stl.h>
4+
5+
#include "sequential_order_keeper.hpp"
6+
#include <sycl/sycl.hpp>
7+
8+
PYBIND11_MODULE(_seq_order_keeper, m)
9+
{
10+
py::class_<SequentialOrder>(m, "_OrderManager")
11+
.def(py::init<std::size_t>())
12+
.def(py::init<>())
13+
.def(py::init<SequentialOrder>())
14+
.def("get_num_submitted_events",
15+
&SequentialOrder::get_num_submitted_events)
16+
.def("get_num_host_task_events",
17+
&SequentialOrder::get_num_host_task_events)
18+
.def("get_submitted_events", &SequentialOrder::get_submitted_events)
19+
.def("get_host_task_events", &SequentialOrder::get_host_task_events)
20+
.def("add_to_both_events", &SequentialOrder::add_to_both_events)
21+
.def("add_vector_to_both_events",
22+
&SequentialOrder::add_vector_to_both_events)
23+
.def("add_to_host_task_events",
24+
&SequentialOrder::add_to_host_task_events)
25+
.def("add_to_submitted_events",
26+
&SequentialOrder::add_to_submitted_events)
27+
.def("wait", &SequentialOrder::wait,
28+
py::call_guard<py::gil_scoped_release>());
29+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#pragma once
2+
#include <sycl/sycl.hpp>
3+
4+
#include <algorithm>
5+
#include <vector>
6+
7+
namespace
8+
{
9+
bool is_event_complete(const sycl::event &e)
10+
{
11+
constexpr auto exec_complete = sycl::info::event_command_status::complete;
12+
13+
const auto status =
14+
e.get_info<sycl::info::event::command_execution_status>();
15+
return (status == exec_complete);
16+
}
17+
} // namespace
18+
19+
class SequentialOrder
20+
{
21+
private:
22+
std::vector<sycl::event> host_task_events;
23+
std::vector<sycl::event> submitted_events;
24+
25+
void prune_complete()
26+
{
27+
const auto &ht_it =
28+
std::remove_if(host_task_events.begin(), host_task_events.end(),
29+
is_event_complete);
30+
host_task_events.erase(ht_it, host_task_events.end());
31+
32+
const auto &sub_it =
33+
std::remove_if(submitted_events.begin(), submitted_events.end(),
34+
is_event_complete);
35+
submitted_events.erase(sub_it, submitted_events.end());
36+
}
37+
38+
public:
39+
SequentialOrder() : host_task_events{}, submitted_events{} {}
40+
SequentialOrder(size_t n) : host_task_events{}, submitted_events{}
41+
{
42+
host_task_events.reserve(n);
43+
submitted_events.reserve(n);
44+
}
45+
46+
SequentialOrder(const SequentialOrder &other)
47+
: host_task_events(other.host_task_events),
48+
submitted_events(other.submitted_events)
49+
{
50+
prune_complete();
51+
}
52+
SequentialOrder(SequentialOrder &&other)
53+
: host_task_events{}, submitted_events{}
54+
{
55+
host_task_events = std::move(other.host_task_events);
56+
submitted_events = std::move(other.submitted_events);
57+
prune_complete();
58+
}
59+
60+
SequentialOrder &operator=(const SequentialOrder &other)
61+
{
62+
host_task_events = other.host_task_events;
63+
submitted_events = other.submitted_events;
64+
65+
prune_complete();
66+
return *this;
67+
}
68+
69+
SequentialOrder &operator=(SequentialOrder &&other)
70+
{
71+
if (this != &other) {
72+
host_task_events = std::move(other.host_task_events);
73+
submitted_events = std::move(other.submitted_events);
74+
prune_complete();
75+
}
76+
return *this;
77+
}
78+
79+
size_t get_num_submitted_events() const
80+
{
81+
return submitted_events.size();
82+
}
83+
84+
const std::vector<sycl::event> &get_host_task_events()
85+
{
86+
prune_complete();
87+
return host_task_events;
88+
}
89+
90+
/*
91+
const std::vector<sycl::event> & get_host_task_events() const {
92+
return host_task_events;
93+
}
94+
*/
95+
96+
size_t get_num_host_task_events() const
97+
{
98+
return host_task_events.size();
99+
}
100+
101+
const std::vector<sycl::event> &get_submitted_events()
102+
{
103+
prune_complete();
104+
return submitted_events;
105+
}
106+
107+
/*
108+
const std::vector<sycl::event> & get_submitted_events() const {
109+
return submitted_events;
110+
}
111+
*/
112+
113+
void add_to_both_events(const sycl::event &ht_ev,
114+
const sycl::event &comp_ev)
115+
{
116+
prune_complete();
117+
if (!is_event_complete(ht_ev))
118+
host_task_events.push_back(ht_ev);
119+
if (!is_event_complete(comp_ev))
120+
submitted_events.push_back(comp_ev);
121+
}
122+
123+
void add_vector_to_both_events(const std::vector<sycl::event> &ht_evs,
124+
const std::vector<sycl::event> &comp_evs)
125+
{
126+
prune_complete();
127+
for (const auto &e : ht_evs) {
128+
if (!is_event_complete(e))
129+
host_task_events.push_back(e);
130+
}
131+
for (const auto &e : comp_evs) {
132+
if (!is_event_complete(e))
133+
submitted_events.push_back(e);
134+
}
135+
}
136+
137+
void add_to_host_task_events(const sycl::event &ht_ev)
138+
{
139+
prune_complete();
140+
if (!is_event_complete(ht_ev)) {
141+
host_task_events.push_back(ht_ev);
142+
}
143+
}
144+
145+
void add_to_submitted_events(const sycl::event &comp_ev)
146+
{
147+
prune_complete();
148+
if (!is_event_complete(comp_ev)) {
149+
submitted_events.push_back(comp_ev);
150+
}
151+
}
152+
153+
template <std::size_t num>
154+
void add_list_to_host_task_events(const sycl::event (&ht_events)[num])
155+
{
156+
prune_complete();
157+
for (size_t i = 0; i < num; ++i) {
158+
const auto &e = ht_events[i];
159+
if (!is_event_complete(e))
160+
host_task_events.push_back(e);
161+
}
162+
}
163+
164+
template <std::size_t num>
165+
void add_list_to_submitted_events(const sycl::event (&comp_events)[num])
166+
{
167+
prune_complete();
168+
for (size_t i = 0; i < num; ++i) {
169+
const auto &e = comp_events[i];
170+
if (!is_event_complete(e))
171+
submitted_events.push_back(e);
172+
}
173+
}
174+
175+
void wait()
176+
{
177+
sycl::event::wait(submitted_events);
178+
sycl::event::wait(host_task_events);
179+
prune_complete();
180+
}
181+
};

0 commit comments

Comments
 (0)