Skip to content

Commit 0b6b3b8

Browse files
[SYCL][Graph] Add a shortcut for adding leaves as dependencies (#11855)
Adds a node property that allows users to easily add all leaves of a graph as dependencies when creating a node with the explicit API. Updates the spec with this new feature. Adds unitests that check this behaviour.
1 parent e15ebd0 commit 0b6b3b8

File tree

7 files changed

+234
-5
lines changed

7 files changed

+234
-5
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_graph.asciidoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ class depends_on {
336336
depends_on(NodeTN... nodes);
337337
};
338338
339+
class depends_on_all_leaves {
340+
public:
341+
depends_on_all_leaves() = default;
342+
};
343+
339344
} // namespace node
340345
} // namespace property
341346
@@ -481,6 +486,21 @@ class depends_on {
481486
}
482487
----
483488

489+
==== Depends-On-All-Leaves Property
490+
The API for explicitly adding nodes to a `command_graph` includes a
491+
`property_list` parameter. This extension defines the `depends_on_all_leaves`
492+
property to be passed here. `depends_on_all_leaves` provides a shortcut for
493+
adding all the current leaves of a graph as dependencies.
494+
[source,c++]
495+
----
496+
namespace sycl::ext::oneapi::experimental::property::node {
497+
class depends_on_all_leaves {
498+
public:
499+
depends_on_all_leaves();
500+
};
501+
}
502+
----
503+
484504
=== Graph
485505

486506
This extension adds a new `command_graph` object which follows the

sycl/include/sycl/detail/property_helper.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ enum DataLessPropKind {
4646
QueueSubmissionImmediate = 21,
4747
GraphAssumeDataOutlivesBuffer = 22,
4848
GraphAssumeBufferOutlivesGraph = 23,
49+
GraphDependOnAllLeaves = 24,
4950
// Indicates the last known dataless property.
50-
LastKnownDataLessPropKind = 23,
51+
LastKnownDataLessPropKind = 24,
5152
// Exceeding 32 may cause ABI breaking change on some of OSes.
5253
DataLessPropKindSize = 32
5354
};

sycl/include/sycl/ext/oneapi/experimental/graph.hpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ class depends_on : public ::sycl::detail::PropertyWithData<
137137
const std::vector<::sycl::ext::oneapi::experimental::node> MDeps;
138138
};
139139

140+
/// Property used to to add all previous graph leaves as dependencies when
141+
/// creating a new node with command_graph::add().
142+
class depends_on_all_leaves : public ::sycl::detail::DataLessProperty<
143+
::sycl::detail::GraphDependOnAllLeaves> {
144+
public:
145+
depends_on_all_leaves() = default;
146+
};
147+
140148
} // namespace node
141149
} // namespace property
142150

@@ -159,9 +167,17 @@ class __SYCL_EXPORT modifiable_command_graph {
159167
node add(const property_list &PropList = {}) {
160168
if (PropList.has_property<property::node::depends_on>()) {
161169
auto Deps = PropList.get_property<property::node::depends_on>();
162-
return addImpl(Deps.get_dependencies());
170+
node Node = addImpl(Deps.get_dependencies());
171+
if (PropList.has_property<property::node::depends_on_all_leaves>()) {
172+
addGraphLeafDependencies(Node);
173+
}
174+
return Node;
163175
}
164-
return addImpl({});
176+
node Node = addImpl({});
177+
if (PropList.has_property<property::node::depends_on_all_leaves>()) {
178+
addGraphLeafDependencies(Node);
179+
}
180+
return Node;
165181
}
166182

167183
/// Add a command-group node to the graph.
@@ -171,9 +187,17 @@ class __SYCL_EXPORT modifiable_command_graph {
171187
template <typename T> node add(T CGF, const property_list &PropList = {}) {
172188
if (PropList.has_property<property::node::depends_on>()) {
173189
auto Deps = PropList.get_property<property::node::depends_on>();
174-
return addImpl(CGF, Deps.get_dependencies());
190+
node Node = addImpl(CGF, Deps.get_dependencies());
191+
if (PropList.has_property<property::node::depends_on_all_leaves>()) {
192+
addGraphLeafDependencies(Node);
193+
}
194+
return Node;
195+
}
196+
node Node = addImpl(CGF, {});
197+
if (PropList.has_property<property::node::depends_on_all_leaves>()) {
198+
addGraphLeafDependencies(Node);
175199
}
176-
return addImpl(CGF, {});
200+
return Node;
177201
}
178202

179203
/// Add a dependency between two nodes.
@@ -247,6 +271,11 @@ class __SYCL_EXPORT modifiable_command_graph {
247271
/// @return Node added to the graph.
248272
node addImpl(const std::vector<node> &Dep);
249273

274+
/// Adds all graph leaves as dependencies
275+
/// @param Node Destination node to which the leaves of the graph will be
276+
/// added as dependencies.
277+
void addGraphLeafDependencies(node Node);
278+
250279
template <class Obj>
251280
friend decltype(Obj::impl)
252281
sycl::detail::getSyclObjImpl(const Obj &SyclObject);

sycl/source/detail/graph_impl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,19 @@ node modifiable_command_graph::addImpl(std::function<void(handler &)> CGF,
726726
return sycl::detail::createSyclObjFromImpl<node>(NodeImpl);
727727
}
728728

729+
void modifiable_command_graph::addGraphLeafDependencies(node Node) {
730+
// Find all exit nodes in the current graph and add them to the dependency
731+
// vector
732+
std::shared_ptr<detail::node_impl> DstImpl =
733+
sycl::detail::getSyclObjImpl(Node);
734+
graph_impl::WriteLock Lock(impl->MMutex);
735+
for (auto &NodeImpl : impl->MNodeStorage) {
736+
if ((NodeImpl->MSuccessors.size() == 0) && (NodeImpl != DstImpl)) {
737+
impl->makeEdge(NodeImpl, DstImpl);
738+
}
739+
}
740+
}
741+
729742
void modifiable_command_graph::make_edge(node &Src, node &Dest) {
730743
std::shared_ptr<detail::node_impl> SenderImpl =
731744
sycl::detail::getSyclObjImpl(Src);

sycl/test/abi/sycl_symbols_linux.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,6 +3734,7 @@ _ZN4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graph7addImplES
37343734
_ZN4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graph9make_edgeERNS3_4nodeES7_
37353735
_ZN4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graphC1ERKNS0_7contextERKNS0_6deviceERKNS0_13property_listE
37363736
_ZN4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graphC2ERKNS0_7contextERKNS0_6deviceERKNS0_13property_listE
3737+
_ZN4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graph24addGraphLeafDependenciesENS3_4nodeE
37373738
_ZNK4sycl3_V13ext6oneapi12experimental6detail24modifiable_command_graph11print_graphENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEb
37383739
_ZN4sycl3_V13ext6oneapi12experimental9image_memC1ERKNS3_16image_descriptorERKNS0_5queueE
37393740
_ZN4sycl3_V13ext6oneapi12experimental9image_memC1ERKNS3_16image_descriptorERKNS0_6deviceERKNS0_7contextE

sycl/test/abi/sycl_symbols_windows.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@
873873
?add@device_global_map@detail@_V1@sycl@@YAXPEBXPEBD@Z
874874
?add@host_pipe_map@detail@_V1@sycl@@YAXPEBXPEBD@Z
875875
?add@modifiable_command_graph@detail@experimental@oneapi@ext@_V1@sycl@@QEAA?AVnode@34567@AEBVproperty_list@67@@Z
876+
?addGraphLeafDependencies@modifiable_command_graph@detail@experimental@oneapi@ext@_V1@sycl@@IEAAXVnode@34567@@Z
876877
?addHostAccessorAndWait@detail@_V1@sycl@@YAXPEAVAccessorImplHost@123@@Z
877878
?addHostSampledImageAccessorAndWait@detail@_V1@sycl@@YAXPEAVSampledImageAccessorImplHost@123@@Z
878879
?addHostUnsampledImageAccessorAndWait@detail@_V1@sycl@@YAXPEAVUnsampledImageAccessorImplHost@123@@Z

sycl/unittests/Extensions/CommandGraph.cpp

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,170 @@ TEST_F(CommandGraphTest, EnqueueMultipleBarrier) {
15001500
}
15011501
}
15021502

1503+
TEST_F(CommandGraphTest, DependencyLeavesKeyword1) {
1504+
auto Node1Graph = Graph.add(
1505+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1506+
auto Node2Graph = Graph.add(
1507+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1508+
auto Node3Graph = Graph.add(
1509+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1510+
1511+
auto EmptyNode =
1512+
Graph.add([&](sycl::handler &cgh) { /*empty node */ },
1513+
{experimental::property::node::depends_on_all_leaves()});
1514+
1515+
auto GraphImpl = sycl::detail::getSyclObjImpl(Graph);
1516+
1517+
// Check the graph structure
1518+
// (1) (2) (3)
1519+
// \ | /
1520+
// \ | /
1521+
// (E)
1522+
ASSERT_EQ(GraphImpl->MRoots.size(), 3lu);
1523+
auto EmptyImpl = sycl::detail::getSyclObjImpl(EmptyNode);
1524+
ASSERT_EQ(EmptyImpl->MPredecessors.size(), 3lu);
1525+
ASSERT_EQ(EmptyImpl->MSuccessors.size(), 0lu);
1526+
1527+
auto Node1Impl = sycl::detail::getSyclObjImpl(Node1Graph);
1528+
ASSERT_EQ(Node1Impl->MSuccessors.size(), 1lu);
1529+
ASSERT_EQ(Node1Impl->MSuccessors[0].lock(), EmptyImpl);
1530+
auto Node2Impl = sycl::detail::getSyclObjImpl(Node2Graph);
1531+
ASSERT_EQ(Node2Impl->MSuccessors.size(), 1lu);
1532+
ASSERT_EQ(Node2Impl->MSuccessors[0].lock(), EmptyImpl);
1533+
auto Node3Impl = sycl::detail::getSyclObjImpl(Node3Graph);
1534+
ASSERT_EQ(Node3Impl->MSuccessors.size(), 1lu);
1535+
ASSERT_EQ(Node3Impl->MSuccessors[0].lock(), EmptyImpl);
1536+
}
1537+
1538+
TEST_F(CommandGraphTest, DependencyLeavesKeyword2) {
1539+
auto Node1Graph = Graph.add(
1540+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1541+
auto Node2Graph = Graph.add(
1542+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1543+
auto Node3Graph = Graph.add(
1544+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1545+
auto Node4Graph = Graph.add(
1546+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); },
1547+
{experimental::property::node::depends_on(Node3Graph)});
1548+
1549+
auto EmptyNode =
1550+
Graph.add([&](sycl::handler &cgh) { /*empty node */ },
1551+
{experimental::property::node::depends_on_all_leaves()});
1552+
1553+
auto GraphImpl = sycl::detail::getSyclObjImpl(Graph);
1554+
1555+
// Check the graph structure
1556+
// (1) (2) (3)
1557+
// \ | /
1558+
// \ | (4)
1559+
// \| /
1560+
// (E)
1561+
ASSERT_EQ(GraphImpl->MRoots.size(), 3lu);
1562+
auto EmptyImpl = sycl::detail::getSyclObjImpl(EmptyNode);
1563+
ASSERT_EQ(EmptyImpl->MPredecessors.size(), 3lu);
1564+
ASSERT_EQ(EmptyImpl->MSuccessors.size(), 0lu);
1565+
1566+
auto Node1Impl = sycl::detail::getSyclObjImpl(Node1Graph);
1567+
ASSERT_EQ(Node1Impl->MSuccessors.size(), 1lu);
1568+
ASSERT_EQ(Node1Impl->MSuccessors[0].lock(), EmptyImpl);
1569+
auto Node2Impl = sycl::detail::getSyclObjImpl(Node2Graph);
1570+
ASSERT_EQ(Node2Impl->MSuccessors.size(), 1lu);
1571+
ASSERT_EQ(Node2Impl->MSuccessors[0].lock(), EmptyImpl);
1572+
auto Node3Impl = sycl::detail::getSyclObjImpl(Node3Graph);
1573+
ASSERT_EQ(Node3Impl->MSuccessors.size(), 1lu);
1574+
1575+
auto Node4Impl = sycl::detail::getSyclObjImpl(Node4Graph);
1576+
ASSERT_EQ(Node4Impl->MPredecessors.size(), 1lu);
1577+
ASSERT_EQ(Node4Impl->MSuccessors.size(), 1lu);
1578+
ASSERT_EQ(Node4Impl->MSuccessors[0].lock(), EmptyImpl);
1579+
}
1580+
1581+
TEST_F(CommandGraphTest, DependencyLeavesKeyword3) {
1582+
auto Node1Graph = Graph.add(
1583+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1584+
auto Node2Graph = Graph.add(
1585+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1586+
auto EmptyNode =
1587+
Graph.add([&](sycl::handler &cgh) { /*empty node */ },
1588+
{experimental::property::node::depends_on_all_leaves()});
1589+
auto Node3Graph = Graph.add(
1590+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); },
1591+
{experimental::property::node::depends_on(Node1Graph)});
1592+
auto Node4Graph = Graph.add(
1593+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); },
1594+
{experimental::property::node::depends_on(EmptyNode)});
1595+
1596+
auto GraphImpl = sycl::detail::getSyclObjImpl(Graph);
1597+
1598+
// Check the graph structure
1599+
// (1)(2)
1600+
// |\ |
1601+
// | (E)
1602+
// (3) |
1603+
// (4)
1604+
ASSERT_EQ(GraphImpl->MRoots.size(), 2lu);
1605+
auto EmptyImpl = sycl::detail::getSyclObjImpl(EmptyNode);
1606+
ASSERT_EQ(EmptyImpl->MPredecessors.size(), 2lu);
1607+
ASSERT_EQ(EmptyImpl->MSuccessors.size(), 1lu);
1608+
1609+
auto Node1Impl = sycl::detail::getSyclObjImpl(Node1Graph);
1610+
auto Node2Impl = sycl::detail::getSyclObjImpl(Node2Graph);
1611+
ASSERT_EQ(Node1Impl->MSuccessors.size(), 2lu);
1612+
ASSERT_EQ(Node2Impl->MSuccessors.size(), 1lu);
1613+
ASSERT_EQ(Node2Impl->MSuccessors[0].lock(), EmptyImpl);
1614+
1615+
auto Node3Impl = sycl::detail::getSyclObjImpl(Node3Graph);
1616+
ASSERT_EQ(Node3Impl->MPredecessors.size(), 1lu);
1617+
ASSERT_EQ(Node3Impl->MPredecessors[0].lock(), Node1Impl);
1618+
1619+
auto Node4Impl = sycl::detail::getSyclObjImpl(Node4Graph);
1620+
ASSERT_EQ(Node4Impl->MPredecessors.size(), 1lu);
1621+
ASSERT_EQ(Node4Impl->MPredecessors[0].lock(), EmptyImpl);
1622+
}
1623+
1624+
TEST_F(CommandGraphTest, DependencyLeavesKeyword4) {
1625+
auto Node1Graph = Graph.add(
1626+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1627+
auto Node2Graph = Graph.add(
1628+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1629+
auto EmptyNode =
1630+
Graph.add([&](sycl::handler &cgh) { /*empty node */ },
1631+
{experimental::property::node::depends_on_all_leaves()});
1632+
auto Node3Graph = Graph.add(
1633+
[&](sycl::handler &cgh) { cgh.single_task<TestKernel<>>([]() {}); });
1634+
auto EmptyNode2 =
1635+
Graph.add([&](sycl::handler &cgh) { /*empty node */ },
1636+
{experimental::property::node::depends_on_all_leaves()});
1637+
1638+
auto GraphImpl = sycl::detail::getSyclObjImpl(Graph);
1639+
1640+
// Check the graph structure
1641+
// (1)(2)
1642+
// \/
1643+
// (E1) (3)
1644+
// \ /
1645+
// (E2)
1646+
ASSERT_EQ(GraphImpl->MRoots.size(), 3lu);
1647+
auto EmptyImpl = sycl::detail::getSyclObjImpl(EmptyNode);
1648+
ASSERT_EQ(EmptyImpl->MPredecessors.size(), 2lu);
1649+
ASSERT_EQ(EmptyImpl->MSuccessors.size(), 1lu);
1650+
1651+
auto Node1Impl = sycl::detail::getSyclObjImpl(Node1Graph);
1652+
ASSERT_EQ(Node1Impl->MSuccessors.size(), 1lu);
1653+
ASSERT_EQ(Node1Impl->MSuccessors[0].lock(), EmptyImpl);
1654+
auto Node2Impl = sycl::detail::getSyclObjImpl(Node2Graph);
1655+
ASSERT_EQ(Node2Impl->MSuccessors.size(), 1lu);
1656+
ASSERT_EQ(Node2Impl->MSuccessors[0].lock(), EmptyImpl);
1657+
1658+
auto EmptyImpl2 = sycl::detail::getSyclObjImpl(EmptyNode2);
1659+
auto Node3Impl = sycl::detail::getSyclObjImpl(Node3Graph);
1660+
ASSERT_EQ(Node3Impl->MPredecessors.size(), 0lu);
1661+
ASSERT_EQ(Node3Impl->MSuccessors.size(), 1lu);
1662+
ASSERT_EQ(Node3Impl->MSuccessors[0].lock(), EmptyImpl2);
1663+
1664+
ASSERT_EQ(EmptyImpl2->MPredecessors.size(), 2lu);
1665+
}
1666+
15031667
TEST_F(CommandGraphTest, FusionExtensionExceptionCheck) {
15041668
queue Q{ext::codeplay::experimental::property::queue::enable_fusion{}};
15051669

0 commit comments

Comments
 (0)