1.. _use_nested_flow_graphs:
2
3Use Nested Flow Graphs
4======================
5
6
7In addition to nesting algorithms within a flow graph node, it is also
8possible to nest flow graphs. For example, below there is a graph ``g`` with
9two nodes, ``a`` and ``b``. When node ``a`` receives a message, it constructs and
10executes an inner dependence graph. When node ``b`` receives a message, it
11constructs and executes an inner data flow graph:
12
13
14::
15
16
17     graph g;
18       function_node< int, int > a( g, unlimited, []( int i ) -> int {
19           graph h;
20           node_t n1( h, [=]( msg_t ) { cout << "n1: " << i << "\n"; } );
21           node_t n2( h, [=]( msg_t ) { cout << "n2: " << i << "\n"; } );
22           node_t n3( h, [=]( msg_t ) { cout << "n3: " << i << "\n"; } );
23           node_t n4( h, [=]( msg_t ) { cout << "n4: " << i << "\n"; } );
24           make_edge( n1, n2 );
25           make_edge( n1, n3 );
26           make_edge( n2, n4 );
27           make_edge( n3, n4 );
28           n1.try_put(continue_msg());
29           h.wait_for_all();
30           return i;
31       } );
32       function_node< int, int > b( g, unlimited, []( int i ) -> int {
33           graph h;
34           function_node< int, int > m1( h, unlimited, []( int j ) -> int {
35               cout << "m1: " << j << "\n";
36               return j;
37           } );
38           function_node< int, int > m2( h, unlimited, []( int j ) -> int {
39               cout << "m2: " << j << "\n";
40               return j;
41           } );
42           function_node< int, int > m3( h, unlimited, []( int j ) -> int {
43               cout << "m3: " << j << "\n";
44               return j;
45           } );
46           function_node< int, int > m4( h, unlimited, []( int j ) -> int {
47               cout << "m4: " << j << "\n";
48               return j;
49           } );
50           make_edge( m1, m2 );
51           make_edge( m1, m3 );
52           make_edge( m2, m4 );
53           make_edge( m3, m4 );
54           m1.try_put(i);
55           h.wait_for_all();
56           return i;
57       } );
58       make_edge( a, b );
59       for ( int i = 0; i < 3; ++i ) {
60           a.try_put(i);
61       }
62       g.wait_for_all();
63
64
65If the nested graph remains unchanged in structure between invocations
66of the node, it is redundant to construct it each time. Reconstructing
67the graph only adds overhead to the execution. You can modify the
68example above, for example, to have node ``b`` reuse a graph that is
69persistent across its invocations:
70
71
72::
73
74
75     graph h;
76       function_node< int, int > m1( h, unlimited, []( int j ) -> int {
77           cout << "m1: " << j << "\n";
78           return j;
79       } );
80       function_node< int, int > m2( h, unlimited, []( int j ) -> int {
81           cout << "m2: " << j << "\n";
82           return j;
83       } );
84       function_node< int, int > m3( h, unlimited, []( int j ) -> int {
85           cout << "m3: " << j << "\n";
86           return j;
87       } );
88       function_node< int, int > m4( h, unlimited, []( int j ) -> int {
89           cout << "m4: " << j << "\n";
90           return j;
91       } );
92       make_edge( m1, m2 );
93       make_edge( m1, m3 );
94       make_edge( m2, m4 );
95       make_edge( m3, m4 );
96
97
98       graph g;
99       function_node< int, int > a( g, unlimited, []( int i ) -> int {
100           graph h;
101           node_t n1( h, [=]( msg_t ) { cout << "n1: " << i << "\n"; } );
102           node_t n2( h, [=]( msg_t ) { cout << "n2: " << i << "\n"; } );
103           node_t n3( h, [=]( msg_t ) { cout << "n3: " << i << "\n"; } );
104           node_t n4( h, [=]( msg_t ) { cout << "n4: " << i << "\n"; } );
105           make_edge( n1, n2 );
106           make_edge( n1, n3 );
107           make_edge( n2, n4 );
108           make_edge( n3, n4 );
109           n1.try_put(continue_msg());
110           h.wait_for_all();
111           return i;
112       } );
113       function_node< int, int > b( g, unlimited, [&]( int i ) -> int {
114           m1.try_put(i);
115           h.wait_for_all(); // optional since h is not destroyed
116           return i;
117       } );
118       make_edge( a, b );
119       for ( int i = 0; i < 3; ++i ) {
120           a.try_put(i);
121       }
122       g.wait_for_all();
123
124
125It is only necessary to call ``h.wait_for_all()`` at the end of each
126invocation of ``b``'s body in our modified code, if you wish for this ``b``'s
127body to block until the inner graph is done. In the first implementation
128of ``b``, it was necessary to call ``h.wait_for_all`` at the end of each
129invocation since the graph was destroyed at the end of the scope. So it
130would be valid in the body of ``b`` above to call ``m1.try_put(i)`` and then
131return without waiting for ``h`` to become idle.
132
133