Skip to content

Commit 7858877

Browse files
Fábio MestreEwan Crawford
authored andcommitted
[SYCL][Graph] Add specification for kernel binary updates
Adds the kernel binary update feature to the sycl graph specification. This introduces a new dynamic_command_group class which can be used to update the command-group function of a kernel nodes in graphs.
1 parent 242f16b commit 7858877

File tree

2 files changed

+275
-23
lines changed

2 files changed

+275
-23
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_graph.asciidoc

Lines changed: 230 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,114 @@ Parameters:
556556

557557
|===
558558

559+
==== Dynamic Command Groups
560+
561+
[source,c++]
562+
----
563+
namespace ext::oneapi::experimental {
564+
class dynamic_command_group {
565+
public:
566+
dynamic_command_group(
567+
command_graph<graph_state::modifiable> graph,
568+
const std::vector<std::function<void(handler &)>>& cgfList);
569+
570+
size_t get_active_cgf();
571+
void set_active_cgf(size_t cgfIndex);
572+
};
573+
----
574+
575+
Dynamic command-groups can be added as nodes to a graph. They provide a mechanism that
576+
allows updating the command-group function of a node after the graph is finalized.
577+
There is always one command-group function in the dynamic command-group that is set
578+
as active. When a dynamic command-group node is executed, the kernel of the active
579+
command-group function will be run and all the other command-group functions in
580+
`cgfList` will be ignored.
581+
582+
See <<executable-graph-update, Executable Graph Update>> for more information
583+
about updating command-groups.
584+
585+
===== Limitations
586+
587+
Dynamic command-groups can only be used to update kernels. Trying to update a command-group
588+
function that contains other operations will result in an error.
589+
590+
All the command-group functions in a dynamic command-group must have identical dependencies.
591+
It is not allowed for a dynamic command-group to have command-group functions that would
592+
result in a change to the graph topology when set to active. In practice, this means that
593+
any calls to `handler.depends_on()` must be identical for all the command-group functions
594+
in a dynamic command-group.
595+
596+
Table {counter: tableNumber}. Member functions of the `dynamic_command_group` class.
597+
[cols="2a,a"]
598+
|===
599+
|Member Function|Description
600+
601+
|
602+
[source,c++]
603+
----
604+
dynamic_command_group(
605+
command_graph<graph_state::modifiable> graph,
606+
const std::vector<std::function<void(handler &)>>& cgfList);
607+
----
608+
609+
|Constructs a dynamic command-group object that can be added as a node to a `command_graph`.
610+
611+
Parameters:
612+
613+
* `graph` - Graph to be associated with this `dynamic_command_group`.
614+
* `cgfList` - The list of command-group functions that can be activated for this dynamic command-group.
615+
The command-group function at index 0 will be active by default.
616+
617+
Exceptions:
618+
619+
* Throws synchronously with error code `invalid` if the graph wasn't created with
620+
the `property::graph::assume_buffer_outlives_graph` property and the `dynamic_command_group`
621+
is created with command-group functions that use buffers. See the
622+
<<assume-buffer-outlives-graph-property, Assume-Buffer-Outlives-Graph>>
623+
property for more information.
624+
625+
* Throws with error code `invalid` if the `dynamic_command_group` is created with
626+
command-group functions that are not kernel executions.
627+
628+
* Throws with error code `invalid` if the command-group functions in `cgfList` have
629+
event dependencies that are incompatible with each other and would result in
630+
different graph topologies when set to active.
631+
632+
|
633+
[source,c++]
634+
----
635+
size_t get_active_cgf();
636+
----
637+
|Returns the index of the currently active command-group function in this
638+
`dynamic_command_group`.
639+
640+
|
641+
[source,c++]
642+
----
643+
void set_active_cgf(size_t cgfIndex);
644+
----
645+
| Sets the command-group function with index `cgfIndex` as active. The index of the
646+
command-group function in a `dynamic_command_group` is identical to its index in the
647+
`cgfList` vector when it was passed to the `dynamic_command_group` constructor.
648+
649+
This change will be reflected immediately in the modifiable graph which contains this
650+
`dynamic_command_group`. The new value will not be reflected in any executable graphs
651+
created from that modifiable graph until `command_graph::update()` is called, passing
652+
the modified nodes, or a new executable graph is finalized from the modifiable graph.
653+
654+
Setting `cgfIndex` to the index of the currently active command-group function is
655+
a no-op.
656+
657+
Parameters:
658+
659+
* `cgfIndex` - The index of the command-group function that should be set as active.
660+
661+
Exceptions:
662+
663+
* Throw with error code `invalid` if `cgfIndex` is not a valid index.
664+
665+
|===
666+
559667
==== Depends-On Property
560668

561669
[source,c++]
@@ -631,6 +739,8 @@ public:
631739
template<typename T>
632740
node add(T cgf, const property_list& propList = {});
633741
742+
node add(dynamic_command_group& dynamicCG, const property_list& propList = {});
743+
634744
void make_edge(node& src, node& dest);
635745
636746
void print_graph(std::string path, bool verbose = false) const;
@@ -711,21 +821,39 @@ Updates to a graph will be scheduled after any in-flight executions of the same
711821
graph and will not affect previous submissions of the same graph. The user is
712822
not required to wait on any previous submissions of a graph before updating it.
713823

714-
The only type of nodes that are currently able to be updated in a graph are
715-
kernel execution nodes.
716-
717-
The aspects of a kernel execution node that can be configured during update are:
718-
719-
* Parameters to the kernel.
720-
* Execution ND-Range of the kernel.
721-
722824
To update an executable graph, the `property::graph::updatable` property must
723825
have been set when the graph was created during finalization. Otherwise, an
724826
exception will be thrown if a user tries to update an executable graph. This
725827
guarantee allows the backend to provide a more optimized implementation, if
726828
possible.
727829

728-
===== Individual Node Update
830+
===== Supported Features
831+
832+
The only types of nodes that are currently able to be updated in a graph are
833+
kernel execution nodes.
834+
835+
There are two different API's that can be used to update a graph:
836+
837+
* <<individual-node-update, Individual Node Update>> which allows updating
838+
individual nodes of a command-graph.
839+
* <<whole-graph-update, Whole Graph Update>> which allows updating the
840+
entirety of the graph simultaneously by using another graph as a
841+
reference.
842+
843+
The aspects of a kernel execution node that can be changed during update are
844+
different depending on the API used to perform the update:
845+
846+
* For the <<individual-node-update, Individual Node Update>> API it's possible to update
847+
the kernel function, the parameters to the kernel, and the ND-Range.
848+
* For the <<whole-graph-update, Whole Graph Update>> API, only the parameters of the kernel
849+
and the ND-Range can be updated.
850+
851+
===== Individual Node Update [[individual-node-update]]
852+
853+
Individual nodes of an executable graph can be updated directly. Depending on the attribute
854+
of the node that requires updating, different API's should be used:
855+
856+
====== Parameter Updates
729857

730858
Parameters to individual nodes in a graph in the `executable` state can be
731859
updated between graph executions using dynamic parameters. A `dynamic_parameter`
@@ -739,14 +867,6 @@ Parameter updates are performed using a `dynamic_parameter` instance by calling
739867
not registered, even if they use the same parameter value as a
740868
`dynamic_parameter`.
741869

742-
The other node configuration that can be updated is the execution range of the
743-
kernel, this can be set through `node::update_nd_range()` or
744-
`node::update_range()` but does not require any prior registration.
745-
746-
The executable graph can then be updated by passing the updated nodes to
747-
`command_graph<graph_state::executable>::update(node& node)` or
748-
`command_graph<graph_state::executable>::update(const std::vector<node>& nodes)`.
749-
750870
Since the structure of the graph became fixed when finalizing, updating
751871
parameters on a node will not change the already defined dependencies between
752872
nodes. This is important to note when updating buffer parameters to a node,
@@ -762,6 +882,41 @@ dynamic parameter for the buffer can be registered with all the nodes which
762882
use the buffer as a parameter. Then a single `dynamic_parameter::update()` call
763883
will maintain the graphs data dependencies.
764884

885+
====== Execution Range Updates
886+
887+
Another configuration that can be updated is the execution range of the
888+
kernel, this can be set through `node::update_nd_range()` or
889+
`node::update_range()` but does not require any prior registration.
890+
891+
An alternative way to update the execution range of a node is to do so while
892+
updating command groups as described in the next section.
893+
894+
====== Command Group Updates
895+
896+
The command-groups of a kernel node can be updated using dynamic command-groups.
897+
Dynamic command-groups allow replacing the command-group function of a kernel
898+
node with a different one. This effectively allows updating the kernel function
899+
and/or the kernel execution range.
900+
901+
Command-group updates are performed by creating an instance of the
902+
`dynamic_command_group` class. A dynamic command-group is created with a modifiable
903+
state graph and a list of possible command-group functions. Command-group functions
904+
within a dynamic command-group can then be set to active by using the member function
905+
`dynamic_command_group::set_active_cgf()`.
906+
907+
Dynamic command-groups are compatible with dynamic parameters. This means that
908+
dynamic parameters can be used in command-group functions that are part of
909+
dynamic command-groups. Updates to such dynamic parameters will be reflected
910+
in the command-group functions once they are activated.
911+
912+
====== Committing Updates
913+
914+
Updating a node using the methods mentioned above will take effect immediately
915+
for nodes in modifiable command-graphs. However, for graphs that are in the executable
916+
state, in order to commit the update, the updated nodes must be passed to
917+
`command_graph<graph_state::executable>::update(node& node)` or
918+
`command_graph<graph_state::executable>::update(const std::vector<node>& nodes)`.
919+
765920
===== Whole Graph Update [[whole-graph-update]]
766921

767922
A graph in the executable state can have all of its nodes updated using the
@@ -1042,6 +1197,42 @@ Exceptions:
10421197
|
10431198
[source,c++]
10441199
----
1200+
node add(dynamic_command_group& dynamicCG, const property_list& propList = {});
1201+
----
1202+
1203+
| Adds the dynamic command-group `dynamicCG` as a node to the graph and sets the
1204+
current active command-group function in `dynamicCG` as the executable for future
1205+
executions of this graph node.
1206+
1207+
The current active command-group function in `dynamicCG` will be executed asynchronously
1208+
when the graph is submitted to a queue. The requisites of this command-group
1209+
function will be used to identify any dependent nodes in the graph
1210+
to form edges with. The other command-group functions in `dynamicCG` will be captured
1211+
into the graph but will not be executed in a graph submission unless they are
1212+
set to active.
1213+
1214+
Constraints:
1215+
1216+
* This member function is only available when the `command_graph` state is
1217+
`graph_state::modifiable`.
1218+
1219+
Parameters:
1220+
1221+
* `dynamicCG` - Dynamic command-group object to be added as a node.
1222+
1223+
* `propList` - Zero or more properties can be provided to the constructed node
1224+
via an instance of `property_list`. The `property::node::depends_on` property
1225+
can be passed here with a list of nodes to create dependency edges on.
1226+
1227+
Returns: The dynamic command-group object node which has been added to the graph.
1228+
1229+
Exceptions:
1230+
1231+
* Throws synchronously with error code `invalid` if a queue is recording
1232+
commands to the graph.
1233+
|
1234+
[source,c++]
1235+
----
10451236
void make_edge(node& src, node& dest);
10461237
----
10471238

@@ -1157,8 +1348,9 @@ void update(node& node);
11571348
----
11581349

11591350
| Updates an executable graph node that corresponds to `node`. `node` must be a
1160-
kernel execution node. Kernel arguments and the ND-range of the node will be
1161-
updated inside the executable graph to reflect the current values in `node`.
1351+
kernel execution node. The command-group function of the node will be updated,
1352+
inside the executable graph, to reflect the current values in `node`. This
1353+
includes the kernel function, the kernel nd-range and the kernel parameters.
11621354

11631355
Updating these values will not change the structure of the graph.
11641356

@@ -1190,9 +1382,10 @@ void update(const std::vector<node>& nodes);
11901382
----
11911383

11921384
| Updates all executable graph nodes that corresponds to the nodes contained in
1193-
`nodes`. All nodes must be kernel nodes. Kernel arguments and the ND-range of
1194-
each node will be updated inside the executable graph to reflect the current
1195-
values in each node in `nodes`.
1385+
`nodes`. All nodes must be kernel nodes. The command-group function of each node
1386+
will be updated, inside the executable graph, to reflect the current values in
1387+
`nodes`. This includes the kernel function, the kernel nd-range and the kernel
1388+
parameters".
11961389

11971390
Updating these values will not change the structure of the graph.
11981391

@@ -1749,6 +1942,10 @@ the call to `queue::submit()` or `command_graph::add()` along with the calls to
17491942
handler functions and this will not be reflected on future executions of the
17501943
graph.
17511944

1945+
Similarly, any command-group function inside a `dynamic_command_group` will be
1946+
evaluated once, in index order, when submitted to the graph using
1947+
`command_graph::add()`.
1948+
17521949
Any code like this should be moved to a separate host-task and added to the
17531950
graph via the recording or explicit APIs in order to be compatible with this
17541951
extension.
@@ -1980,7 +2177,7 @@ can be used to add nodes to a graph when creating a graph from queue recording.
19802177
== Examples and Usage Guide
19812178

19822179
Detailed code examples and usage guidelines are provided in the
1983-
link:../../SYCLGraphUsageGuide.md[SYCL Graph Usage Guide].
2180+
link:../../syclgraph/SYCLGraphUsageGuide.md[SYCL Graph Usage Guide].
19842181

19852182
== Future Direction [[future-direction]]
19862183

@@ -2120,6 +2317,16 @@ to ensure this is desired and makes sense to users.
21202317

21212318
**UNRESOLVED** Needs more discussion.
21222319

2320+
=== Updatable command-groups in the Record & Replay API:
2321+
2322+
Currently the only way to update command-groups in a graph is to use the
2323+
Explicit API. There is a limitation in some backends that requires all
2324+
the command-groups used for updating to be specified before the graph
2325+
is finalized. This restriction makes it hard to implement the
2326+
Record & Replay API in a performant manner.
2327+
2328+
**UNRESOLVED** Needs more discussion.
2329+
21232330
=== Multi Device Graph
21242331

21252332
Allow an executable graph to contain nodes targeting different devices.

sycl/doc/syclgraph/SYCLGraphUsageGuide.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,51 @@ node kernelNode = myGraph.add([&](handler& cgh) {
453453
dynParamAccessor.update(bufferB.get_access());
454454
```
455455

456+
### Dynamic Command Groups
457+
458+
Example showing how a graph with a dynamic command group node can be updated.
459+
460+
```cpp
461+
queue Queue{};
462+
exp_ext::command_graph Graph{Queue.get_context(), Queue.get_device()};
463+
464+
int *PtrA = malloc_device<int>(1024, Queue);
465+
int *PtrB = malloc_device<int>(1024, Queue)​
466+
467+
auto CgfA = [&](handler &cgh) {
468+
cgh.parallel_for(1024, [=](item<1> Item) {
469+
PtrA[Item.get_id()] = 1;​
470+
});
471+
};
472+
473+
auto CgfB = [&](handler &cgh) {
474+
cgh.parallel_for(512, [=](item<1> Item) {
475+
PtrB[Item.get_id()] = 2;
476+
});
477+
};
478+
479+
// Construct a dynamic command-group with CgfA as the active cgf (index 0).
480+
auto DynamicCG = exp_ext::dynamic_command_group(Graph, {CgfA, CgfB});
481+
482+
// Create a dynamic command-group graph node.
483+
auto DynamicCGNode = Graph.add(DynamicCG);
484+
485+
auto ExecGraph = Graph.finalize(exp_ext::property::graph::updatable{});
486+
487+
// The graph will execute CgfA.
488+
Queue.ext_oneapi_graph(ExecGraph).wait();
489+
490+
// Sets CgfB as active in the dynamic command-group (index 1).
491+
DynamicCG.set_active_cgf(1);
492+
493+
// Calls update to update the executable graph node with the changes to DynamicCG.
494+
ExecGraph.update(DynamicCGNode);
495+
496+
// The graph will execute CgfB.
497+
Queue.ext_oneapi_graph(ExecGraph).wait();
498+
```
499+
500+
456501
### Whole Graph Update
457502
458503
Example that shows recording and updating several nodes with different

0 commit comments

Comments
 (0)