Skip to content

Commit 47d2b2c

Browse files
vossmjpaleksei-fedotovakukanov
authored
Add tasking documentation to user guide (#1791)
Co-authored-by: Aleksei Fedotov <[email protected]> Co-authored-by: Alexey Kukanov <[email protected]>
1 parent 9aa510a commit 47d2b2c

9 files changed

+540
-0
lines changed

doc/main/tbb_userguide/Cancellation_Without_An_Exception.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,29 @@ The example below shows how to use ``current_context()->cancel_group_execution()
4444
return 0;
4545
}
4646

47+
task_group Cancellation Example
48+
===============================
49+
50+
The interface to a ``task_group`` provides shortcuts to access its asoociated ``task_group_context``.
51+
The function ``oneapi::tbb::task_group::cancel()`` cancels the ``task_group_context`` associated
52+
with the ``task_group`` instance. And the free function
53+
``oneapi::tbb::is_current_task_group_canceling()`` returns ``true`` if the innermost ``task_group``
54+
executing on the calling thread is cancelling its tasks.
55+
56+
Here is an example of how to use task cancellation with ``oneapi::tbb::task_group``. This code
57+
uses ``struct TreeNode`` and the function ``sequential_tree_search`` that are described in
58+
:ref:`creating_tasks_with_parallel_invoke`.
59+
60+
The function ``parallel_tree_search_cancellable_impl`` cancels the ``task_group`` when a result
61+
is found.
62+
63+
.. literalinclude:: ./examples/task_examples.cpp
64+
:language: c++
65+
:start-after: /*begin_parallel_search_cancellation*/
66+
:end-before: /*end_parallel_search_cancellation*/
67+
68+
The call to ``tg.cancel()`` cancels the tasks in the ``task_group`` that have been submitted but
69+
have not started to execute. The task scheduler will not execute these tasks. Those tasks that
70+
have already started will not be interrupted but they can query the cancellation status of the
71+
``task_group`` by calling ``oneapi::tbb::is_current_task_group_canceling()`` and exit early if
72+
they detect cancellation.
27.5 KB
Loading
29.8 KB
Loading
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.. _Parallelizing_with_Tasks:
2+
3+
Parallelizing with Tasks
4+
========================
5+
6+
When parallel loops or the flow graph are not sufficient, the |full_name|
7+
library supports parallelization directly with tasks. Tasks
8+
can be created using the function ``oneapi::tbb::parallel_invoke`` or
9+
the class ``oneapi::tbb::task_group``.
10+
11+
12+
.. toctree::
13+
:maxdepth: 4
14+
15+
../tbb_userguide/creating_tasks_with_parallel_invoke
16+
../tbb_userguide/creating_tasks_with_task_group
17+
../tbb_userguide/task_group_thread_safety
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.. _creating_tasks_with_parallel_invoke:
2+
3+
Creating Tasks with parallel_invoke
4+
===================================
5+
6+
Suppose you want to search a binary tree for the node that contains a specific value.
7+
Here is sequential code to do this:
8+
9+
Nodes are represented by ``struct TreeNode``:
10+
11+
.. literalinclude:: ./examples/task_examples.cpp
12+
:language: c++
13+
:start-after: /*begin_treenode*/
14+
:end-before: /*end_treenode*/
15+
16+
The function ``serial_tree_search`` is a recursive algorithm that checks the current node
17+
and, if the value is not found, calls itself on the left and right subtree.
18+
19+
.. literalinclude:: ./examples/task_examples.cpp
20+
:language: c++
21+
:start-after: /*begin_search_serial*/
22+
:end-before: /*end_search_serial*/
23+
24+
25+
To improve performance, you can use ``oneapi::tbb::parallel_invoke`` to search the tree
26+
in parallel:
27+
28+
A recursive base case is used after a minimum size threshold is reached to avoid parallel overheads.
29+
Since more than one thread can call the base case concurrently as part of the same tree, ``result``
30+
is held in an atomic variable.
31+
32+
.. literalinclude:: ./examples/task_examples.cpp
33+
:language: c++
34+
:start-after: /*begin_sequential_tree_search*/
35+
:end-before: /*end_sequential_tree_search*/
36+
37+
The function ``oneapi::tbb::parallel_invoke`` runs multiple independent tasks in parallel.
38+
Here is a function ``parallel_invoke_search``, where two lambdas are passed that define tasks
39+
that search the left and right subtrees of the current node in parallel:
40+
41+
.. literalinclude:: ./examples/task_examples.cpp
42+
:language: c++
43+
:start-after: /*begin_parallel_invoke_search*/
44+
:end-before: /*end_parallel_invoke_search*/
45+
46+
If the value is found, the pointer to the node that contains the value is stored
47+
in the ``std::atomic<TreeNode*> result``. This example uses recursion to create many tasks, instead of
48+
just two. The depth of the parallel recursion is limited by the ``depth_threshold`` parameter. After this depth is
49+
reached, the search falls back to a sequential approach. The value of ``result`` is periodically checked
50+
to see if the value has been found by other concurrent tasks, and if so, the search in the current task is
51+
terminated. Because multiple threads may access ``result`` concurrently, an atomic variable is used, even
52+
from the sequential base case.
53+
54+
Because ``oneapi::tbb::parallel_invoke`` is a fork-join algorithm, each level of the recursion does not
55+
complete until both the left and right subtrees have completed.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.. _creating_tasks_with_task_group:
2+
3+
Creating Tasks with task_group
4+
==============================
5+
6+
The |full_name| library supports parallelization directly with tasks. The class
7+
``oneapi::tbb::task_group`` is used to run and wait for tasks in a less
8+
structured way than ``oneapi::tbb::parallel_invoke``. It is useful when you want to
9+
create a set of tasks that can be run in parallel, but you do not know how many there
10+
will be in advance.
11+
12+
Here is code that uses ``oneapi::tbb::task_group`` to implement a parallel search in a
13+
binary tree. This code uses ``struct TreeNode`` and the function ``sequential_tree_search``
14+
that are described in :ref:`creating_tasks_with_parallel_invoke`.
15+
16+
In ``parallel_tree_search_impl``, ``task_group::run`` is used to create new tasks for searching
17+
in the subtrees. The recursion does not wait on the ``task_group`` at each level.
18+
19+
.. literalinclude:: ./examples/task_examples.cpp
20+
:language: c++
21+
:start-after: /*begin_parallel_search*/
22+
:end-before: /*end_parallel_search*/
23+
24+
This example uses recursion to create many tasks. The depth of the parallel recursion is
25+
limited by the ``depth_threshold`` parameter. After this depth is reached, no new tasks
26+
are created. The value of ``result`` is periodically checked to see if the value has been
27+
found by other concurrent tasks, and if so, the search in the current task is terminated.
28+
29+
In this example, tasks that execute within the ``task_group tg`` create additional tasks
30+
by calling ``run`` on the same ``task_group`` object. These calls are thread-safe and the
31+
call to ``tg.wait()`` will block until all of these tasks are complete. Although
32+
tasks might be added from different worker threads, these additions are logically nested
33+
within the top-most calls to ``tg.run``. There is therefore no race in adding these tasks from
34+
the worker threads and waiting for them in the main thread.

0 commit comments

Comments
 (0)