Skip to content

Commit 9d45bb5

Browse files
committed
Before updating wavefront.
Merge remote-tracking branch 'refs/remotes/origin/dev' into dev
2 parents 94e5771 + e8e3eff commit 9d45bb5

11 files changed

Lines changed: 161 additions & 111 deletions

File tree

doc/cookbook/a.out

-77 KB
Binary file not shown.

doc/cookbook/dispatch.md

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ you need to dispatch it to threads for execution
55
In this tutorial, we will show you how to execute a
66
task dependency graph.
77

8-
+ [Graph and Topology](#Graph-and-Topology)
9-
+ [Blocking Execution](#Blocking-Execution)
10-
+ [Non-blocking Execution](#Non-blocking-Execution)
11-
+ [Wait on Topologies](#Wait-on-Topologies)
12-
+ [Example 1: Multiple Dispatches](#Example-1-Multiple-Dispatches)
13-
+ [Example 2: Connect Two Dependency Graphs](#Example-2-Connect-Two-Dependency-Graphs)
8+
+ [Graph and Topology](#graph-and-topology)
9+
+ [Blocking Execution](#blocking-execution)
10+
+ [Non-blocking Execution](#non-blocking-execution)
11+
+ [Wait on Topologies](#wait-on-topologies)
12+
+ [Lifetime of a Graph](#lifetime-of-a-graph)
13+
+ [Example 1: Multiple Dispatches](#example-1-multiple-dispatches)
14+
+ [Example 2: Connect Two Dependency Graphs](#example-2-connect-two-dependency-graphs)
1415

1516
# Graph and Topology
1617

@@ -138,6 +139,98 @@ After Line 11, there are two topologies in the taskflow object.
138139
Calling the method `wait_for_topologies` blocks the
139140
program until both graph complete.
140141

142+
# Lifetime of a Graph
143+
144+
In Cpp-Taskflow, the lifetime of a task sticks with its parent graph.
145+
The lifetime of a task mostly refers to the user-given callable objects,
146+
including those captured by a lambda expression.
147+
When a graph is destroyed, all of its tasks are destroyed.
148+
Consider the following example that uses
149+
[std::shared_ptr][std::shared_ptr] to demonstrate the lifetime of a graph and
150+
the impact on its task.
151+
152+
```cpp
153+
1: tf::Taskflow tf;
154+
2:
155+
3: auto ptr = std::make_shared<int>(0);
156+
4:
157+
5: std::cout << "reference count before A and B: " << ptr.use_count() << '\n';
158+
6:
159+
7: auto A = tf.silent_emplace([ptr] () {
160+
8: std::cout << "reference count at A: " << ptr.use_count() << '\n';
161+
9: });
162+
10:
163+
11: auto B = tf.silent_emplace([ptr] () {
164+
12: std::cout << "reference count at B: " << ptr.use_count() << '\n';
165+
13: });
166+
14:
167+
15: A.precede(B);
168+
16:
169+
17: std::cout << "reference count after A and B: " << ptr.use_count() << '\n';
170+
18:
171+
19: tf.wait_for_all();
172+
20:
173+
21: std::cout << "reference count after A and B: " << ptr.use_count() << '\n';
174+
```
175+
176+
The output is as follows:
177+
178+
```bash
179+
reference count before A and B: 1
180+
reference count after A and B: 3
181+
reference count at A: 3
182+
reference count at B: 3
183+
reference count after A and B: 1
184+
```
185+
186+
In Line 5, we created a shared pointer object with one reference count on itself.
187+
After Line 7-13, the reference count increases by two because
188+
task A and task B both capture a copy of the shared pointer.
189+
The lifetime of a task has nothing to do with its dependency constraints,
190+
as shown in Line 8, Line 12, and Line 17.
191+
However, Line 19 dispatches the graph to threads and cleans up its data structure
192+
upon finish, including all associated tasks.
193+
Therefore, the reference count in Line 21 drops down to one (owner at Line 3).
194+
195+
Now let's use the same example but dispatch the graph asynchronously.
196+
197+
```cpp
198+
1: tf::Taskflow tf;
199+
2:
200+
3: auto ptr = std::make_shared<int>(0);
201+
4:
202+
5: std::cout << "reference count before A and B: " << ptr.use_count() << '\n';
203+
6:
204+
7: auto A = tf.silent_emplace([ptr] () {
205+
8: std::cout << "reference count at A: " << ptr.use_count() << '\n';
206+
9: });
207+
10:
208+
11: auto B = tf.silent_emplace([ptr] () {
209+
12: std::cout << "reference count at B: " << ptr.use_count() << '\n';
210+
13: });
211+
14:
212+
15: A.precede(B);
213+
16:
214+
17: std::cout << "reference count after A and B: " << ptr.use_count() << '\n';
215+
18:
216+
19: tf.dispatch().get(); // dispatch the graph without destroying it
217+
20:
218+
21: std::cout << "reference count after A and B: " << ptr.use_count() << '\n';
219+
```
220+
221+
In Line 19, we replace `wait_for_all` with `disaptch` to dispatch the graph asynchronously
222+
without cleaning up its data structures upon completion.
223+
In other words, task A and task B remains un-destructed after Line 19,
224+
and the reference count at this point remains three.
225+
226+
```bash
227+
reference count before A and B: 1
228+
reference count after A and B: 3
229+
reference count at A: 3
230+
reference count at B: 3
231+
reference count after A and B: 3
232+
```
233+
141234
# Example 1: Multiple Dispatches
142235

143236
The example below demonstrates how to create multiple task dependency graphs and
@@ -253,3 +346,7 @@ The result of the variable `sum` ends up being the summation over
253346
the integer sequence `[0, 1, 2, ..., 1024)`.
254347

255348

349+
* * *
350+
351+
[std::shared_ptr]: https://en.cppreference.com/w/cpp/memory/shared_ptr
352+

doc/cookbook/hello_world.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Hello World
1+
# Write Your First Cpp-Taskflow Program
22

33
In this tutorial, we are going to demonstrate how to write a Cpp-Taskflow's
44
"hello world" program.

doc/cookbook/parallel_for.cpp

Lines changed: 0 additions & 23 deletions
This file was deleted.

doc/cookbook/parallel_for.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
# Parallelize a For Loop
1+
# Create a Paralle For-loop Graph
22

33
In this tutorial, we are going to demonstrate how to use Cpp-Taskflow
4-
to run a for loop in parallel.
4+
to run a for-loop in parallel.
55

6-
+ [Range-based For Loop](#range-based-for-loop)
7-
+ [Index-based For Loop](#index-based-for-loop)
6+
+ [Range-based For-loop](#range-based-for-loop)
7+
+ [Index-based For-loop](#index-based-for-loop)
88
+ [Example 1: Parallel Map](#example-1-parallel-map)
99
+ [Example 2: Pipeline a Parallel For](#example-2-pipeline-a-parallel-for)
1010

11-
# Range-based For Loop
11+
# Range-based For-loop
1212

1313
Cpp-Taskflow has a STL-style method `parallel_for`
1414
that takes a range of items and applies a callable to each of the item in parallel.
@@ -85,7 +85,7 @@ and would like to enable more efficient parallelization.
8585
## Construct the Graph Explicitly
8686

8787
You can explicitly construct a dependency graph that represents a parallel execution
88-
of a for loop.
88+
of a for-loop.
8989
using only the basic methods `silent_emplace` and `precede`.
9090

9191

@@ -102,9 +102,9 @@ for(auto item : items) {
102102
}
103103
```
104104

105-
# Index-based For Loop
105+
# Index-based For-loop
106106

107-
To parallelize a for loop based on index, you can use the capture feature of C++ lambda.
107+
To parallelize a for-loop based on index, you can use the capture feature of C++ lambda.
108108

109109
```cpp
110110
1: auto S = tf.silent_emplace([] () {}).name("S");

doc/cookbook/reduce.cpp

Lines changed: 0 additions & 53 deletions
This file was deleted.

doc/cookbook/reduce.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Reduce a Container of Items in Parallel
1+
# Create a Parallel Reduction Graph
22

33
Parallel tasks normally produce some quantity that needs to be combined or *reduced*
44
through particular operations, for instance, sum.

doc/cookbook/task.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# Understand the Task Construct
1+
# Understand the Task
22

33
In this tutorial, we are going to demonstrate the basic construct of
44
a task dependency graph - *Task*.
55

66
+ [Create a Task](#create-a-task)
77
+ [Access the Result of a Task](#access-the-result-of-a-task)
88
+ [Create Multiple Tasks at One Time](#create-multiple-tasks-at-one-time)
9+
+ [Lifetime of a Task](#lifetime-of-a-task)
910
+ [Example 1: Create Multiple Dependency Graphs](#example-1-create-multiple-dependency-graphs)
1011
+ [Example 2: Modify Task Attributes](#example-2-modify-task-attributes)
1112

@@ -26,11 +27,13 @@ to create a task.
2627
2728
Debrief:
2829
+ Line 1 creates an empty task
29-
+ Line 2 creates a task from a given callable object
30-
+ Line 3 creates a task from a given callable object and returns an additional [std::future][std::future] object for users
30+
+ Line 2 creates a task from a given callable object and returns a task handle
31+
+ Line 3 creates a task from a given callable object and returns, in addition to a task handle,
32+
a [std::future][std::future] object for the program
3133
to access the return value when the task finishes
3234
33-
Each time you create a task, the taskflow object adds a node to the present graph
35+
Each time you create a task, including an empty one,
36+
the taskflow object adds a node to the present graph
3437
and returns a *task handle* of type `tf::Task`.
3538
A task handle is a lightweight object
3639
that wraps up a particular node in a graph
@@ -106,6 +109,18 @@ and create multiple tasks at one time.
106109
5: );
107110
```
108111
112+
# Lifetime of A Task
113+
114+
A task lives with its graph, and is not destroyed until its parent graph
115+
gets cleaned up.
116+
A task belongs to only a graph at a time.
117+
The lifetime of a task mostly refers to the user-given callable object,
118+
including captured values.
119+
As long as the graph is alive,
120+
all the associated tasks remain their existence.
121+
We recommend the users to read [Lifetime of a Graph][Lifetime of a Graph].
122+
123+
109124
---
110125
111126
# Example 1: Create Multiple Dependency Graphs
@@ -243,4 +258,5 @@ Only the latest information will be used.
243258
[std::function]: https://en.cppreference.com/w/cpp/utility/functional/function
244259
[std::invoke]: https://en.cppreference.com/w/cpp/utility/functional/invoke
245260
[std::future]: https://en.cppreference.com/w/cpp/thread/future
261+
[Lifetime of a Graph]: dispatch.md#lifetime-of-a-graph
246262

doc/faq.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
This page summarizes a list of frequently asked questions about Cpp-Taskflow.
44
If you cannot find a solution here, please post an issue [here][Github issues].
55

6-
+ [General Questions](#General-Questions)
7-
+ [Compilation Issues](#Compilation-Issues)
8-
+ [Programming Questions](#Programming-Questions)
6+
+ [General Questions](#general-questions)
7+
+ [Compilation Issues](#compilation-issues)
8+
+ [Programming Questions](#programming-questions)
99

1010
---
1111

@@ -96,6 +96,24 @@ tf::Taskflow(N); // N workers, N+1 threads in the program.
9696
9797
If there is no worker threads in the pool, the master thread will do all the works by itself.
9898
99+
## Q: What is the difference between a Task and a Task Handle?
100+
101+
A task in Cpp-Taskflow is a callable object
102+
for which the operation [std::invoke][std::invoke] is applicable.
103+
It can be either
104+
a functor, a lambda expression, a bind expression,
105+
or a class objects with `operator()` overloaded.
106+
107+
A task handle is a lightweight object
108+
that wraps up a particular node in a graph
109+
and provides a set of methods for you to assign different attributes to the task
110+
such as adding dependencies, naming, and assigning a new work.
111+
112+
## Q: What is the Lifetime of a Task and a Graph?
113+
114+
The lifetime of a task sticks with its parent graph. A task is not destroyed until its parent
115+
graph is destroyed.
116+
99117
## Q: Is taskflow thread-safe?
100118
101119
No, the taskflow object is not thread-safe. You can't create tasks from multiple threads
@@ -143,18 +161,13 @@ for(int i=0; i<N; ++i) {
143161
tf.wait_for_all();
144162
```
145163
146-
## Q: What is a Task?
147-
148-
A `Task` is a lightweight handle associated with an internal graph node
149-
for users to build task dependencies.
150-
We do not allow users to explicitly access the internal node but using this wrapper
151-
to assign different attributes to a graph node.
152164
153165
* * *
154166
[Github issues]: https://github.com/cpp-taskflow/cpp-taskflow/issues
155167
[OpenTimer]: https://github.com/OpenTimer/OpenTimer
156168
[README]: ../README.md
157169
[C++17]: https://en.wikipedia.org/wiki/C%2B%2B17
170+
[std::invoke]: https://en.cppreference.com/w/cpp/utility/functional/invoke
158171
159172
160173

doc/home.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ software architecture, C++ API, and library usages.
1212

1313
# Cookbook
1414

15-
+ [Write your First Cpp-Taskflow Program](cookbook/hello_world.md)
16-
+ [Understand the Task Construct](cookbook/task.md)
15+
+ [Write Your First Cpp-Taskflow Program](cookbook/hello_world.md)
16+
+ [Understand the Task](cookbook/task.md)
1717
+ [Execute a Task Dependency Graph](cookbook/dispatch.md)
18-
+ [Parallelize a For Loop](cookbook/parallel_for.md)
19-
+ [Reduce a Container of Items in Parallel](cookbook/reduce.md)
18+
+ [Create a Parallel For-loop Graph](cookbook/parallel_for.md)
19+
+ [Create a Parallel Reduction Graph](cookbook/reduce.md)
2020
+ [Spawn a Task Dependency Graph at Runtime](cookbook/dynamic_tasking.md)
2121

2222
# Application Gallery

0 commit comments

Comments
 (0)