xref: /oneTBB/doc/main/tbb_userguide/Nodes.rst (revision 67c11716)
1.. _Nodes:
2
3Flow Graph Basics: Nodes
4========================
5
6
7A node is a class that inherits from oneapi::tbb::flow::graph_node and also
8typically inherits from oneapi::tbb::flow::sender<T> , oneapi::tbb::flow::receiver<T> or
9both. A node performs some operation, usually on an incoming message and
10may generate zero or more output messages. Some nodes require more than
11one input message or generate more than one output message.
12
13
14While it is possible to define your own node types by inheriting from
15graph_node, sender and receiver, it is more typical that predefined node
16types are used to construct a graph.
17
18
19A ``function_node`` is a predefined type available in ``flow_graph.h`` and
20represents a simple function with one input and one output. The
21constructor for a ``function_node`` takes three arguments:
22
23
24::
25
26
27   template< typename Body> function_node(graph &g, size_t concurrency, Body body)
28
29
30.. container:: tablenoborder
31
32
33   .. list-table::
34      :header-rows: 1
35
36      * -  Parameter
37        -  Description
38      * -  Body
39        -     Type of the body object.
40      * -  g
41        -     The graph the node belongs to.
42      * -  concurrency
43        -     The concurrency limit for the node. You can use the    concurrency limit to control how many invocations of the node are   allowed to proceed concurrently, from 1 (serial) to an unlimited   number.
44      * -  body
45        -     User defined function object, or lambda expression, that    is applied to the incoming message to generate the outgoing message.
46
47
48
49
50Below is code for creating a simple graph that contains a single
51function_node. In this example, a node n is constructed that belongs to
52graph g, and has a second argument of 1, which allows at most 1
53invocation of the node to occur concurrently. The body is a lambda
54expression that prints each value v that it receives, spins for v
55seconds, prints the value again, and then returns v unmodified. The code
56for the function spin_for is not provided.
57
58
59::
60
61
62       graph g;
63       function_node< int, int > n( g, 1, []( int v ) -> int {
64           cout << v;
65           spin_for( v );
66           cout << v;
67           return v;
68       } );
69
70
71After the node is constructed in the example above, you can pass
72messages to it, either by connecting it to other nodes using edges or by
73invoking its function try_put. Using edges is described in the next
74section.
75
76
77::
78
79
80       n.try_put( 1 );
81       n.try_put( 2 );
82       n.try_put( 3 );
83
84
85You can then wait for the messages to be processed by calling
86wait_for_all on the graph object:
87
88
89::
90
91
92       g.wait_for_all();
93
94
95In the above example code, the function_node n was created with a
96concurrency limit of 1. When it receives the message sequence 1, 2 and
973, the node n will spawn a task to apply the body to the first input, 1.
98When that task is complete, it will then spawn another task to apply the
99body to 2. And likewise, the node will wait for that task to complete
100before spawning a third task to apply the body to 3. The calls to
101try_put do not block until a task is spawned; if a node cannot
102immediately spawn a task to process the message, the message will be
103buffered in the node. When it is legal, based on concurrency limits, a
104task will be spawned to process the next buffered message.
105
106
107In the above graph, each message is processed sequentially. If however,
108you construct the node with a different concurrency limit, parallelism
109can be achieved:
110
111
112::
113
114
115       function_node< int, int > n( g, oneapi::tbb::flow::unlimited, []( int v ) -> int {
116           cout << v;
117           spin_for( v );
118           cout << v;
119           return v;
120       } );
121
122
123You can use unlimited as the concurrency limit to instruct the library
124to spawn a task as soon as a message arrives, regardless of how many
125other tasks have been spawned. You can also use any specific value, such
126as 4 or 8, to limit concurrency to at most 4 or 8, respectively. It is
127important to remember that spawning a task does not mean creating a
128thread. So while a graph may spawn many tasks, only the number of
129threads available in the library's thread pool will be used to execute
130these tasks.
131
132
133Suppose you use unlimited in the function_node constructor instead and
134call try_put on the node:
135
136
137::
138
139
140       n.try_put( 1 );
141       n.try_put( 2 );
142       n.try_put( 3 );
143       g.wait_for_all();
144
145
146The library spawns three tasks, each one applying n's lambda expression
147to one of the messages. If you have a sufficient number of threads
148available on your system, then all three invocations of the body will
149occur in parallel. If however, you have only one thread in the system,
150they execute sequentially.
151
152