File: module.dox

package info (click to toggle)
taskflow 3.9.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 45,948 kB
  • sloc: cpp: 39,058; xml: 35,572; python: 12,935; javascript: 1,732; makefile: 59; sh: 16
file content (162 lines) | stat: -rw-r--r-- 4,624 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
namespace tf {

/** @page ModuleAlgorithm Module Algorithm

%Taskflow provides template methods that let users create reusable building blocks
called @em modules.
Users can connect modules together to build more complex parallel algorithms.

@tableofcontents

@section ModuleAlgorithmInclude Include the Header

You need to include the header file, <tt>taskflow/algorithm/module.hpp</tt>,
for creating a module task over a schedulable graph target.

@code{.cpp}
#include <taskflow/algorithm/module.hpp>
@endcode

@section WhatIsAModuleTask What is a Module Task

Similar to @ref ComposableTasking, but in a more generic setting,
the template function, tf::make_module_task,
allows you to create a task over a taskflow graph that can be executed by an executor.
This provides a flexible way to encapsulate and reuse complex task logic within your 
%Taskflow applications.
The example below demonstrates how to create and launch multiple taskflows in parallel 
using asynchronous tasking:

@code{.cpp}
tf::Executor executor;

tf::Taskflow A;
tf::Taskflow B;
tf::Taskflow C;
tf::Taskflow D;

A.emplace([](){ printf("Taskflow A\n"); }); 
B.emplace([](){ printf("Taskflow B\n"); }); 
C.emplace([](){ printf("Taskflow C\n"); }); 
D.emplace([](){ printf("Taskflow D\n"); }); 

// launch the four taskflows using asynchronous tasking
executor.async(tf::make_module_task(A));
executor.async(tf::make_module_task(B));
executor.async(tf::make_module_task(C));
executor.async(tf::make_module_task(D));
executor.wait_for_all();  
@endcode

@dotfile images/module_task_1.dot

Since the four taskflows are launched asynchronously without any dependencies between them,
we can observe any order of the output message:

@code{.shell-session}
# one possible output
Taskflow B
Taskflow C
Taskflow A
Taskflow D

# another possible output
Taskflow D
Taskflow A
Taskflow B
Taskflow C
@endcode

If you need to enforce dependencies among these four taskflows,
you can use dependent-async tasks.
The example below launches the four taskflows one by one in sequential:

@code{.cpp}
tf::Executor executor;

tf::Taskflow A;
tf::Taskflow B;
tf::Taskflow C;
tf::Taskflow D;

A.emplace([](){ printf("Taskflow A\n"); }); 
B.emplace([](){ printf("Taskflow B\n"); }); 
C.emplace([](){ printf("Taskflow C\n"); }); 
D.emplace([](){ printf("Taskflow D\n"); }); 

auto TA = executor.silent_dependent_async(tf::make_module_task(A));
auto TB = executor.silent_dependent_async(tf::make_module_task(B), TA);
auto TC = executor.silent_dependent_async(tf::make_module_task(C), TB);
auto [TD, FD] = executor.dependent_async(tf::make_module_task(D), TC);
FD.get();
@endcode

@dotfile images/module_task_2.dot

@code{.shell-session}
# dependent-async tasks enforce a sequential execution of the four taskflows
Taskflow A
Taskflow B
Taskflow C
Taskflow D
@endcode

The module task maker, tf::make_module_task, functions basically similar to tf::Taskflow::composed_of 
but provides a more generic interface that can be used beyond %Taskflow.
Specifically, the following two approaches achieve the same functionality.

@code{.cpp}
// approach 1: composition using composed_of
tf::Task m1 = taskflow1.composed_of(taskflow2);

// approach 2: composition using make_module_task
tf::Task m1 = taskflow1.emplace(tf::make_module_task(taskflow2));
@endcode

@attention
Similar to tf::Taskflow::composed_of, tf::make_module_task does not assume ownership of 
the provided taskflow but a soft reference.
You are responsible for ensuring that the encapsulated taskflow remains valid
throughout its execution.


@section CreateAModuleTaskOverACustomGraph Create a Module Task over a Custom Graph

In addition to encapsulate taskflow graphs, you can create a module task to schedule 
a custom graph target.
A schedulable target (of type `T`) must define the method `T::graph()` that returns a reference 
to the tf::Graph object managed by `T`.
The following example defines a custom graph that can be scheduled through making module tasks:

@code{.cpp}
struct CustomGraph {
  tf::Graph graph;
  CustomGraph() {
    // use flow builder to inherit all task creation methods in tf::Taskflow
    tf::FlowBuilder builder(graph);
    tf::Task task = builder.emplace([](){
      std::cout << "a task\n";  // static task
    });
  }
  // returns a reference to the graph for taskflow composition
  Graph& graph() { return graph; }
};

CustomGraph target;
executor.async(tf::make_module_task(target));
@endcode

@attention
Users are responsible for ensuring the given custom graph remains valid throughout its execution.
The executor does not assume ownership of the custom graph.


*/

}