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