Skip to content

Commit 21b4ff9

Browse files
removed silent_emplace from the docs
1 parent 4892558 commit 21b4ff9

33 files changed

Lines changed: 481 additions & 517 deletions

README.md

Lines changed: 49 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ int main(){
5555

5656
tf::Taskflow tf;
5757

58-
auto [A, B, C, D] = tf.silent_emplace(
58+
auto [A, B, C, D] = tf.emplace(
5959
[] () { std::cout << "TaskA\n"; }, // task dependency graph
6060
[] () { std::cout << "TaskB\n"; }, //
6161
[] () { std::cout << "TaskC\n"; }, // +---+
@@ -129,17 +129,17 @@ Create a taskflow object to start a task dependency graph.
129129
tf::Taskflow tf;
130130
```
131131

132-
Create a task from a callable object via the method `silent_emplace`
132+
Create a task from a callable object via the method `emplace`
133133
to get a task handle.
134134

135135
```cpp
136-
auto A = tf.silent_emplace([](){ std::cout << "Task A\n"; });
136+
tf::Task A = tf.emplace([](){ std::cout << "Task A\n"; });
137137
```
138138
139139
You can create multiple tasks at one time.
140140
141141
```cpp
142-
auto [A, B, C, D] = tf.silent_emplace(
142+
auto [A, B, C, D] = tf.emplace(
143143
[] () { std::cout << "Task A\n"; },
144144
[] () { std::cout << "Task B\n"; },
145145
[] () { std::cout << "Task C\n"; },
@@ -196,15 +196,15 @@ that spawns three tasks during its execution.
196196

197197
```cpp
198198
// create three regular tasks
199-
auto A = tf.silent_emplace([](){}).name("A");
200-
auto C = tf.silent_emplace([](){}).name("C");
201-
auto D = tf.silent_emplace([](){}).name("D");
199+
tf::Task A = tf.emplace([](){}).name("A");
200+
tf::Task C = tf.emplace([](){}).name("C");
201+
tf::Task D = tf.emplace([](){}).name("D");
202202

203203
// create a subflow graph (dynamic tasking)
204-
auto B = tf.silent_emplace([] (auto& subflow) {
205-
auto B1 = subflow.silent_emplace([](){}).name("B1");
206-
auto B2 = subflow.silent_emplace([](){}).name("B2");
207-
auto B3 = subflow.silent_emplace([](){}).name("B3");
204+
tf::Task B = tf.emplace([] (tf::SubflowBuilder& subflow) {
205+
tf::Task B1 = subflow.emplace([](){}).name("B1");
206+
tf::Task B2 = subflow.emplace([](){}).name("B2");
207+
tf::Task B3 = subflow.emplace([](){}).name("B3");
208208
B1.precede(B3);
209209
B2.precede(B3);
210210
}).name("B");
@@ -246,7 +246,7 @@ To create a subflow for dynamic tasking,
246246
emplace a callable on one argument of type `tf::SubflowBuilder`.
247247

248248
```cpp
249-
auto A = tf.silent_emplace([] (tf::SubflowBuilder& subflow) {});
249+
tf::Task A = tf.emplace([] (tf::SubflowBuilder& subflow) {});
250250
```
251251

252252
Similarly, you can get a [std::future][std::future] object to the execution status of the subflow.
@@ -261,9 +261,9 @@ All graph building methods defined in taskflow
261261
can be used in a subflow builder.
262262

263263
```cpp
264-
auto A = tf.silent_emplace([] (tf::SubflowBuilder& subflow) {
264+
tf::Task A = tf.emplace([] (tf::SubflowBuilder& subflow) {
265265
std::cout << "Task A is spawning two subtasks A1 and A2" << '\n';
266-
auto [A1, A2] = subflow.silent_emplace(
266+
auto [A1, A2] = subflow.emplace(
267267
[] () { std::cout << "subtask A1" << '\n'; },
268268
[] () { std::cout << "subtask A2" << '\n'; }
269269
A1.precede(A2);
@@ -274,21 +274,21 @@ auto A = tf.silent_emplace([] (tf::SubflowBuilder& subflow) {
274274
A subflow can also be nested or recursive. You can create another subflow from
275275
the execution of a subflow and so on.
276276

277-
<img align="right" src="image/nested_subflow.png" width="45%">
277+
<img align="right" src="image/nested_subflow.png" width="40%">
278278

279279
```cpp
280-
auto A = tf.silent_emplace([] (auto& sbf){
280+
tf::Task A = tf.emplace([] (tf::SubflowBuilder& sbf){
281281
std::cout << "A spawns A1 & subflow A2\n";
282-
auto A1 = sbf.silent_emplace([] () {
282+
tf::Task A1 = sbf.emplace([] () {
283283
std::cout << "subtask A1\n";
284284
}).name("A1");
285285

286-
auto A2 = sbf.silent_emplace([] (auto& sbf2){
286+
tf::Task A2 = sbf.emplace([](tf::SubflowBuilder& sbf2){
287287
std::cout << "A2 spawns A2_1 & A2_2\n";
288-
auto A2_1 = sbf2.silent_emplace([] () {
288+
tf::Task A2_1 = sbf2.emplace([] () {
289289
std::cout << "subtask A2_1\n";
290290
}).name("A2_1");
291-
auto A2_2 = sbf2.silent_emplace([] () {
291+
tf::Task A2_2 = sbf2.emplace([] () {
292292
std::cout << "subtask A2_2\n";
293293
}).name("A2_2");
294294
A2_1.precede(A2_2);
@@ -305,7 +305,7 @@ By default, a subflow joins to its parent task.
305305
Depending on applications, you can detach a subflow to enable more parallelism.
306306

307307
```cpp
308-
auto A = tf.silent_emplace([] (tf::SubflowBuilder& subflow) {
308+
tf::Task A = tf.emplace([] (tf::SubflowBuilder& subflow) {
309309
subflow.detach(); // detach this subflow from its parent task A
310310
}); // subflow starts to run after the callable scope
311311
```
@@ -316,23 +316,21 @@ In a joined subflow,
316316
the completion of its parent node is defined as when all tasks
317317
inside the subflow (possibly nested) finish.
318318

319-
<img align="right" src="image/joined_subflow_future.png" width="40%">
319+
<img align="right" src="image/joined_subflow_future.png" width="35%">
320320

321321
```cpp
322322
int value {0};
323323

324324
// create a joined subflow
325-
auto [A, fuA] = tf.emplace([&] (auto& subflow) {
326-
subflow.silent_emplace([&]() {
325+
tf::Task A = tf.emplace([&] (tf::SubflowBuilder& subflow) {
326+
subflow.emplace([&]() {
327327
value = 10;
328328
});
329-
return 100; // some arbitrary value
330329
});
331330

332331
// create a task B after A
333-
auto B = tf.silent_emplace([&] () {
332+
tf::Task B = tf.emplace([&] () {
334333
assert(value == 10);
335-
assert(fuA.wait_for(0s) == std::future_status::ready);
336334
});
337335

338336
// A1 must finish before A and therefore before B
@@ -343,22 +341,22 @@ When a subflow is detached from its parent task, it becomes a parallel
343341
execution line to the current flow graph and will eventually
344342
join to the same topology.
345343

346-
<img align="right" src="image/detached_subflow_future.png" width="40%">
344+
<img align="right" src="image/detached_subflow_future.png" width="35%">
347345

348346
```cpp
349347
int value {0};
350348

351349
// create a detached subflow
352-
auto [A, fuA] = tf.emplace([&] (auto& subflow) {
353-
subflow.silent_emplace([&]() { value = 10; });
350+
tf::Task A = tf.emplace([&] (tf::SubflowBuilder& subflow) {
351+
subflow.emplace([&]() { value = 10; });
354352
subflow.detach();
355-
return 100; // some arbitrary value
356353
});
357354

358355
// create a task B after A
359-
auto B = tf.silent_emplace([&] () {
356+
tf::Task B = tf.emplace([&] () {
360357
// no guarantee for value to be 10 nor fuA ready
361358
});
359+
362360
A.precede(B);
363361
```
364362

@@ -381,17 +379,18 @@ The graph is not dispatched yet and you can dump it to a GraphViz format.
381379
```cpp
382380
// debug.cpp
383381
tf::Taskflow tf(0); // use only the master thread
384-
auto A = tf.silent_emplace([] () {}).name("A");
385-
auto B = tf.silent_emplace([] () {}).name("B");
386-
auto C = tf.silent_emplace([] () {}).name("C");
387-
auto D = tf.silent_emplace([] () {}).name("D");
388-
auto E = tf.silent_emplace([] () {}).name("E");
382+
383+
tf::Task A = tf.emplace([] () {}).name("A");
384+
tf::Task B = tf.emplace([] () {}).name("B");
385+
tf::Task C = tf.emplace([] () {}).name("C");
386+
tf::Task D = tf.emplace([] () {}).name("D");
387+
tf::Task E = tf.emplace([] () {}).name("E");
389388

390389
A.precede(B, C, E);
391390
C.precede(D);
392391
B.precede(D, E);
393392

394-
std::cout << tf.dump();
393+
tf.dump(std::cout);
395394
```
396395
397396
Run the program and inspect whether dependencies are expressed in the right way.
@@ -426,12 +425,12 @@ and then use the `dump_topologies` method.
426425
```cpp
427426
tf::Taskflow tf(0); // use only the master thread
428427

429-
auto A = tf.silent_emplace([](){}).name("A");
428+
tf::Task A = tf.emplace([](){}).name("A");
430429

431430
// create a subflow of two tasks B1->B2
432-
auto B = tf.silent_emplace([] (auto& subflow) {
433-
auto B1 = subflow.silent_emplace([](){}).name("B1");
434-
auto B2 = subflow.silent_emplace([](){}).name("B2");
431+
tf::Task B = tf.emplace([] (tf::SubflowBuilder& subflow) {
432+
tf::Task B1 = subflow.emplace([](){}).name("B1");
433+
tf::Task B2 = subflow.emplace([](){}).name("B2");
435434
B1.precede(B2);
436435
}).name("B");
437436

@@ -441,7 +440,7 @@ A.precede(B);
441440
tf.dispatch().get();
442441

443442
// dump the entire graph (including dynamic tasks)
444-
std::cout << tf.dump_topologies();
443+
tf.dump_topologies(std::cout);
445444
```
446445
447446
# API Reference
@@ -456,8 +455,7 @@ Visit [documentation][wiki] to see the complete list.
456455
| -------- | --------- | ------- | ----------- |
457456
| Taskflow | none | none | construct a taskflow with the worker count equal to max hardware concurrency |
458457
| Taskflow | size | none | construct a taskflow with a given number of workers |
459-
| emplace | callables | tasks, futures | insert nodes to execute the given callables; results can be retrieved from the returned futures |
460-
| silent_emplace | callables | tasks | insert nodes to execute the given callables |
458+
| emplace | callables | tasks | create a task with a given callable(s) |
461459
| placeholder | none | task | insert a node without any work; work can be assigned later |
462460
| linearize | task list | none | create a linear dependency in the given task list |
463461
| parallel_for | beg, end, callable, group | task pair | apply the callable in parallel and group-by-group to the result of dereferencing every iterator in the range |
@@ -474,31 +472,22 @@ Visit [documentation][wiki] to see the complete list.
474472
| dump | none | string | dump the current graph to a string of GraphViz format |
475473
| dump_topologies | none | string | dump dispatched topologies to a string of GraphViz format |
476474
477-
### *emplace/silent_emplace/placeholder*
475+
### *emplace/placeholder*
478476
479-
The main different between `emplace` and `silent_emplace` is the return value.
480-
The method `emplace` gives you a [std::future][std::future] object to retrieve the result of the callable
481-
when the task completes.
477+
You can use `emplace` to create a task for a target callable.
482478
483479
```cpp
484480
// create a task through emplace
485-
auto [task, future] = tf.emplace([](){ return 1; });
486-
tf.wait_for_all();
487-
assert(future.get() == 1);
488-
```
489-
490-
If you don't care the return result, using `silent_emplace` to create a task can give you slightly better performance.
491-
```cpp
492-
// create a task through silent_emplace
493-
auto task = tf.emplace([](){ return; });
481+
tf::Task task = tf.emplace([] () { std::cout << "my task\n"; });
494482
tf.wait_for_all();
495483
```
496484

497485
When task cannot be determined beforehand, you can create a placeholder and assign the calalble later.
486+
498487
```cpp
499488
// create a placeholder and use it to build dependency
500-
auto A = tf.silent_emplace([](){});
501-
auto B = tf.placeholder();
489+
tf::Task A = tf.emplace([](){});
490+
tf::Task B = tf.placeholder();
502491
A.precede(B);
503492

504493
// assign the callable later in the control flow
@@ -771,7 +760,6 @@ The folder `example/` contains several examples and is a great place to learn to
771760
| ------- | ----------- |
772761
| [simple.cpp](./example/simple.cpp) | uses basic task building blocks to create a trivial taskflow graph |
773762
| [debug.cpp](./example/debug.cpp)| inspects a taskflow through the dump method |
774-
| [emplace.cpp](./example/emplace.cpp)| demonstrates the difference between the emplace method and the silent_emplace method |
775763
| [matrix.cpp](./example/matrix.cpp) | creates two set of matrices and multiply each individually in parallel |
776764
| [dispatch.cpp](./example/dispatch.cpp) | demonstrates how to dispatch a task dependency graph and assign a callback to execute |
777765
| [multiple_dispatch.cpp](./example/multiple_dispatch.cpp) | illustrates dispatching multiple taskflow graphs as independent batches (which all run on the same threadpool) |

docs/Cookbook.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ var Cookbook =
99
] ],
1010
[ "C1: Understand the Task", "chapter1.html", [
1111
[ "What is a Task?", "chapter1.html#WhatIsATask", null ],
12-
[ "Access the Result of a Task", "chapter1.html#AccessTheResultOfATask", null ],
1312
[ "Create Multiple Tasks at One Time", "chapter1.html#CreateMultipleTasksAtOneTime", null ],
1413
[ "Lifetime of A Task", "chapter1.html#LifetimeOfATask", null ],
1514
[ "Example 1: Create Multiple Dependency Graphs", "chapter1.html#C1Example1", null ],

docs/Doxyfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,10 @@ INPUT = ../taskflow/taskflow.hpp \
818818
../taskflow/threadpool/speculative_threadpool.hpp \
819819
../taskflow/threadpool/workstealing_threadpool.hpp \
820820
QuickStart.dox \
821+
releases/releases.dox \
822+
releases/master-branch.dox \
823+
releases/release-2.1.0.dox \
824+
releases/release-2.0.0.dox \
821825
cookbook/Cookbook.dox \
822826
cookbook/Chapter0.dox \
823827
cookbook/Chapter1.dox \
@@ -921,7 +925,7 @@ EXAMPLE_RECURSIVE = NO
921925
# that contain images that are to be included in the documentation (see the
922926
# \image command).
923927

924-
IMAGE_PATH = favicon/
928+
IMAGE_PATH = favicon/ ./
925929

926930
# The INPUT_FILTER tag can be used to specify a program that doxygen should
927931
# invoke to filter for each input file. Doxygen will invoke the filter program

docs/FAQ.dox

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Try the tf::Taskflow::dump method to debug the graph before dispatching your tas
9191

9292
@subsection ProgrammingQuestions5 Q5: In the following example where B spawns a joined subflow of two tasks B1 and B2, do they run concurrently with task A?
9393

94-
@image html dynamic_graph.png width=60%
94+
@image html images/dynamic_graph.png width=60%
9595

9696
No. The subflow is spawned during the execution of B, and at this point A must finish
9797
because A precedes B. This gives rise to the fact B1 and B2 must run after A.

docs/QuickStart.dox

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace tf {
77
Cpp-Taskflow is by far faster, more expressive, fewer lines of code, and easier for drop-in integration
88
than existing parallel task programming libraries such as <a href="http://www.nersc.gov/users/software/programming-models/openmp/openmp-tasking/">OpenMP Tasking</a> and Intel <a href="https://www.threadingbuildingblocks.org/tutorial-intel-tbb-flow-graph">Thread Building Block (TBB) FlowGraph</a>.
99

10-
@image html image/performance.jpg width=95%
10+
@image html images/performance.jpg width=95%
1111

1212
Cpp-Taskflow is committed to support both academic and industry research projects,
1313
making it reliable and cost-effective for long-term and large-scale developments.
@@ -41,7 +41,7 @@ int main(){
4141

4242
tf::Taskflow taskflow;
4343

44-
auto [A, B, C, D] = taskflow.silent_emplace(
44+
auto [A, B, C, D] = taskflow.emplace(
4545
[] () { std::cout << "TaskA\n"; }, // task dependency graph
4646
[] () { std::cout << "TaskB\n"; }, //
4747
[] () { std::cout << "TaskC\n"; }, // +---+

docs/chapter0.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ <h1><a class="anchor" id="html"></a>
114114
TaskBasedParallelism Task-based Parallelism</h1>
115115
<p>The traditional loop-level parallelism is simple but hardly allows users to exploit parallelism in more irregular applications such as graph algorithms, incremental flows, recursion, and dynamically-allocated data structures. To address these challenges, parallel programming and libraries are evolving from the tradition loop-based parallelism to the <em>task-based</em> model.</p>
116116
<div class="image">
117-
<img src="images/task-level-parallelism.png" alt="task-level-parallelism.png" width="30%"/>
117+
<img src="task-level-parallelism.png" alt="task-level-parallelism.png" width="30%"/>
118118
</div>
119119
<p>The above figure shows an example <em>task dependency graph</em>. Each node in the graph represents a task unit at function level and each edge indicates the task dependency between a pair of tasks. Task-based model offers a powerful means to express both regular and irregular parallelism in a top-down manner, and provides transparent scaling to large number of cores. In fact, it has been proven, both by the research community and the evolution of parallel programming standards, task-based approach scales the best with future processor generations and architectures.</p>
120120
<h1><a class="anchor" id="ChallengesOfTaskBasedParallelProgramming"></a>

0 commit comments

Comments
 (0)