-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathExamplesFibonacciNumber.html
More file actions
275 lines (273 loc) · 22.3 KB
/
Copy pathExamplesFibonacciNumber.html
File metadata and controls
275 lines (273 loc) · 22.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<!-- HTML header for doxygen 1.13.1-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.13.1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Taskflow: A General-purpose Task-parallel Programming System: Fibonacci Number</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<script type="text/javascript" src="clipboard.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="cookie.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link href="custom.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<td id="projectlogo"><img alt="Logo" src="taskflow_logo.png"/></td>
<td id="projectalign">
<div id="projectname"><a href="https://github.com/taskflow/taskflow" style="color:inherit; text-decoration:none;">Taskflow: A General-purpose Task-parallel Programming System</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.13.1 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search/",'.html');
/* @license-end */
</script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function() { codefold.init(0); });
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function() {
initMenu('',true,false,'search.php','Search',true);
$(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
$(function(){initNavTree('ExamplesFibonacciNumber.html',''); initResizable(true); });
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<div id="MSearchResults">
<div class="SRPage">
<div id="SRIndex">
<div id="SRResults"></div>
<div class="SRStatus" id="Loading">Loading...</div>
<div class="SRStatus" id="Searching">Searching...</div>
<div class="SRStatus" id="NoMatches">No Matches</div>
</div>
</div>
</div>
</div>
<div><div class="header">
<div class="headertitle"><div class="title">Fibonacci Number</div></div>
</div><!--header-->
<div class="contents">
<div class="toc"><h3>Table of Contents</h3>
<ul>
<li class="level1">
<a href="#FibonacciNumberProblem">Problem Formulation</a>
</li>
<li class="level1">
<a href="#RecursiveFibonacciParallelismUsingRuntimeTasking">Recursive Fibonacci with Runtime Tasking</a>
<ul>
<li class="level2">
<a href="#TailRecursionOptimization">Tail Recursion Optimisation</a>
</li>
<li class="level2">
<a href="#FibonacciNumberBenchmarking">Benchmarking</a>
</li>
</ul>
</li>
<li class="level1">
<a href="#RecursiveFibonacciParallelismUsingTaskGroup">Recursive Fibonacci with Task Group</a>
</li>
</ul>
</div>
<div class="textblock"><p>We study recursive task parallelism using the Fibonacci sequence as a concrete example, demonstrating how <a class="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> enables dynamic task creation and cooperative synchronisation without blocking worker threads.</p>
<h1><a class="anchor" id="FibonacciNumberProblem"></a>
Problem Formulation</h1>
<p>The Fibonacci sequence is defined such that each number is the sum of the two preceding ones, starting from 0 and 1:</p>
<p><code>0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...</code></p>
<p>The natural recursive implementation is:</p>
<div class="fragment"><div class="line"><span class="keywordtype">int</span> fibonacci(<span class="keywordtype">int</span> n) {</div>
<div class="line"> <span class="keywordflow">if</span>(n < 2) <span class="keywordflow">return</span> n;</div>
<div class="line"> <span class="keywordflow">return</span> fibonacci(n-1) + fibonacci(n-2);</div>
<div class="line">}</div>
</div><!-- fragment --><p>The two recursive calls are mutually independent and can therefore run in parallel. The challenge is to express this recursive parallelism efficiently — spawning tasks that themselves spawn tasks — without blocking worker threads or creating unbounded overhead.</p>
<h1><a class="anchor" id="RecursiveFibonacciParallelismUsingRuntimeTasking"></a>
Recursive Fibonacci with Runtime Tasking</h1>
<p>A runtime task accepts a <a class="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a>& parameter, giving it a live handle to the scheduling runtime. We use <a class="el" href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05" title="runs the given function asynchronously without returning any future object">tf::Runtime::silent_async</a> to spawn both recursive branches in parallel and <a class="el" href="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4" title="corun all tasks spawned by this runtime with other workers">tf::Runtime::corun</a> to wait for them cooperatively. <code>corun</code> does not block the calling worker — it participates in the work-stealing loop while waiting, so other tasks can run on the same thread:</p>
<div class="fragment"><div class="line"><span class="preprocessor">#include <taskflow/taskflow.hpp></span></div>
<div class="line"> </div>
<div class="line"><span class="keywordtype">size_t</span> fibonacci(<span class="keywordtype">size_t</span> N, <a class="code hl_class" href="classtf_1_1Runtime.html">tf::Runtime</a>& rt) {</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">if</span>(N < 2) <span class="keywordflow">return</span> N;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">size_t</span> res1, res2;</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// spawn both branches as async runtime tasks</span></div>
<div class="line"> rt.<a class="code hl_function" href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05">silent_async</a>([N, &res1](<a class="code hl_class" href="classtf_1_1Runtime.html">tf::Runtime</a>& rt1) {</div>
<div class="line"> res1 = fibonacci(N-1, rt1);</div>
<div class="line"> });</div>
<div class="line"> rt.<a class="code hl_function" href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05">silent_async</a>([N, &res2](<a class="code hl_class" href="classtf_1_1Runtime.html">tf::Runtime</a>& rt2) {</div>
<div class="line"> res2 = fibonacci(N-2, rt2);</div>
<div class="line"> });</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// cooperatively wait for both branches without blocking the worker</span></div>
<div class="line"> rt.<a class="code hl_function" href="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4">corun</a>();</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">return</span> res1 + res2;</div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line"><span class="keywordtype">int</span> main() {</div>
<div class="line"> </div>
<div class="line"> tf::Executor executor;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">size_t</span> N = 30, res;</div>
<div class="line"> executor.<a class="code hl_function" href="classtf_1_1Executor.html#a0461cb2c459c9f9473c72af06af9c701">silent_async</a>([N, &res](tf::Runtime& rt) {</div>
<div class="line"> res = fibonacci(N, rt);</div>
<div class="line"> });</div>
<div class="line"> executor.<a class="code hl_function" href="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85">wait_for_all</a>();</div>
<div class="line"> </div>
<div class="line"> std::cout << N << <span class="stringliteral">"-th Fibonacci number is "</span> << res << <span class="charliteral">'\n'</span>;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">return</span> 0;</div>
<div class="line">}</div>
<div class="ttc" id="aclasstf_1_1Executor_html_a0461cb2c459c9f9473c72af06af9c701"><div class="ttname"><a href="classtf_1_1Executor.html#a0461cb2c459c9f9473c72af06af9c701">tf::Executor::silent_async</a></div><div class="ttdeci">void silent_async(P &&params, F &&func)</div><div class="ttdoc">similar to tf::Executor::async but does not return a future object</div></div>
<div class="ttc" id="aclasstf_1_1Executor_html_ab9aa252f70e9a40020a1e5a89d485b85"><div class="ttname"><a href="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85">tf::Executor::wait_for_all</a></div><div class="ttdeci">void wait_for_all()</div><div class="ttdoc">waits for all tasks to complete</div></div>
<div class="ttc" id="aclasstf_1_1Runtime_html"><div class="ttname"><a href="classtf_1_1Runtime.html">tf::Runtime</a></div><div class="ttdoc">class to create a runtime task</div><div class="ttdef"><b>Definition</b> runtime.hpp:47</div></div>
<div class="ttc" id="aclasstf_1_1Runtime_html_a0ce29efa2106c8c5a1432e4a55ab2e05"><div class="ttname"><a href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05">tf::Runtime::silent_async</a></div><div class="ttdeci">void silent_async(F &&f)</div><div class="ttdoc">runs the given function asynchronously without returning any future object</div><div class="ttdef"><b>Definition</b> runtime.hpp:671</div></div>
<div class="ttc" id="aclasstf_1_1Runtime_html_aba54a7cacffb54f5eb133730d256a7c4"><div class="ttname"><a href="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4">tf::Runtime::corun</a></div><div class="ttdeci">void corun()</div><div class="ttdoc">corun all tasks spawned by this runtime with other workers</div><div class="ttdef"><b>Definition</b> runtime.hpp:646</div></div>
</div><!-- fragment --><p>Each call to <code>fibonacci</code> recursively spawns two async tasks and then waits cooperatively for both to finish. The executor distributes tasks across all available workers using work-stealing, achieving parallelism at every level of the recursion tree. The execution diagram for <code>fibonacci(4)</code> is shown below. The suffixes <em>_1</em> and <em>_2</em> denote the left and right children spawned by their parent runtime:</p>
<div class="dotgraph">
<iframe scrolling="no" frameborder="0" src="dot_fibonacci_4.svg" width="587" height="374"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
<h2><a class="anchor" id="TailRecursionOptimization"></a>
Tail Recursion Optimisation</h2>
<p>Spawning both branches asynchronously doubles the number of async tasks created: one branch is always available for inline computation in the current execution context, so spawning it asynchronously only adds scheduling overhead without increasing parallelism. The standard optimisation is to compute one branch <em>inline</em> (directly in the current context) and spawn only the other asynchronously. This halves the task count, reduces stack pressure, and cuts scheduling overhead at every level of recursion:</p>
<div class="fragment"><div class="line"><span class="keywordtype">size_t</span> fibonacci(<span class="keywordtype">size_t</span> N, <a class="code hl_class" href="classtf_1_1Runtime.html">tf::Runtime</a>& rt) {</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">if</span>(N < 2) <span class="keywordflow">return</span> N;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">size_t</span> res1, res2;</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// spawn only the left branch asynchronously</span></div>
<div class="line"> rt.<a class="code hl_function" href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05">silent_async</a>([N, &res1](<a class="code hl_class" href="classtf_1_1Runtime.html">tf::Runtime</a>& rt1) {</div>
<div class="line"> res1 = fibonacci(N-1, rt1);</div>
<div class="line"> });</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// compute the right branch inline in the current context</span></div>
<div class="line"> res2 = fibonacci(N-2, rt);</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// cooperatively wait for the left branch</span></div>
<div class="line"> rt.<a class="code hl_function" href="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4">corun</a>();</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">return</span> res1 + res2;</div>
<div class="line">}</div>
</div><!-- fragment --><p>The optimised execution diagram for <code>fibonacci(4)</code> is shown below. The right branch at each level has been eliminated, replaced by inline computation:</p>
<div class="dotgraph">
<iframe scrolling="no" frameborder="0" src="dot_fibonacci_4_tail_optimized.svg" width="587" height="374"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
<h2><a class="anchor" id="FibonacciNumberBenchmarking"></a>
Benchmarking</h2>
<p>The table below compares wall-clock runtime of the two implementations across different values of <code>N</code> on a multi-core machine:</p>
<div align="center"> <table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadCenter">N </th><th class="markdownTableHeadCenter">with tail optimisation </th><th class="markdownTableHeadCenter">without tail optimisation </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyCenter">20 </td><td class="markdownTableBodyCenter">0.23 ms </td><td class="markdownTableBodyCenter">0.31 ms </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyCenter">25 </td><td class="markdownTableBodyCenter">2 ms </td><td class="markdownTableBodyCenter">4 ms </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyCenter">30 </td><td class="markdownTableBodyCenter">23 ms </td><td class="markdownTableBodyCenter">42 ms </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyCenter">35 </td><td class="markdownTableBodyCenter">269 ms </td><td class="markdownTableBodyCenter">483 ms </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyCenter">40 </td><td class="markdownTableBodyCenter">3003 ms </td><td class="markdownTableBodyCenter">5124 ms </td></tr>
</table>
</div><p>The performance gap widens as <code>N</code> increases because the number of tasks created grows exponentially. At <code>N</code> = 40, tail optimisation cuts runtime by over 40% by eliminating half the task-creation and scheduling overhead at every recursive level.</p>
<h1><a class="anchor" id="RecursiveFibonacciParallelismUsingTaskGroup"></a>
Recursive Fibonacci with Task Group</h1>
<p><a class="el" href="classtf_1_1TaskGroup.html" title="class to create a task group from a task">tf::TaskGroup</a> offers a lighter-weight alternative to <a class="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> for recursive parallelism from any calling context — no need for the caller itself to be a runtime task. The interface is the same: spawn sub-tasks with <code>silent_async</code>, then call <code>corun</code> to wait cooperatively.</p>
<div class="fragment"><div class="line"><a class="code hl_class" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div>
<div class="line"> </div>
<div class="line">std::function<size_t(<span class="keywordtype">size_t</span>)> fibonacci = [&](<span class="keywordtype">size_t</span> N) -> <span class="keywordtype">size_t</span> {</div>
<div class="line"> <span class="keywordflow">if</span>(N < 2) <span class="keywordflow">return</span> N;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">size_t</span> res1, res2;</div>
<div class="line"> <a class="code hl_class" href="classtf_1_1TaskGroup.html">tf::TaskGroup</a> tg = executor.<a class="code hl_function" href="classtf_1_1Executor.html#a8858a119b9ad697748293c4bb1408853">task_group</a>();</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// spawn left branch; compute right branch inline (tail optimisation)</span></div>
<div class="line"> tg.<a class="code hl_function" href="classtf_1_1TaskGroup.html#acf90acfcaf9468adc56bf647208a9e78">silent_async</a>([N, &res1, &fibonacci]() {</div>
<div class="line"> res1 = fibonacci(N-1);</div>
<div class="line"> });</div>
<div class="line"> res2 = fibonacci(N-2);</div>
<div class="line"> </div>
<div class="line"> tg.<a class="code hl_function" href="classtf_1_1TaskGroup.html#a1f481dc466e3107a08346d1a124677bc">corun</a>(); <span class="comment">// cooperatively wait for the left branch</span></div>
<div class="line"> <span class="keywordflow">return</span> res1 + res2;</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line"><span class="keywordtype">int</span> main() {</div>
<div class="line"> <span class="keywordtype">size_t</span> N = 30;</div>
<div class="line"> <span class="keywordtype">size_t</span> res = executor.<a class="code hl_function" href="classtf_1_1Executor.html#af960048056f7c6b5bc71f4f526f05df7">async</a>([&]() { <span class="keywordflow">return</span> fibonacci(N); }).get();</div>
<div class="line"> std::cout << N << <span class="stringliteral">"-th Fibonacci number is "</span> << res << <span class="charliteral">'\n'</span>;</div>
<div class="line"> <span class="keywordflow">return</span> 0;</div>
<div class="line">}</div>
<div class="ttc" id="aclasstf_1_1Executor_html"><div class="ttname"><a href="classtf_1_1Executor.html">tf::Executor</a></div><div class="ttdoc">class to create an executor</div><div class="ttdef"><b>Definition</b> executor.hpp:62</div></div>
<div class="ttc" id="aclasstf_1_1Executor_html_a8858a119b9ad697748293c4bb1408853"><div class="ttname"><a href="classtf_1_1Executor.html#a8858a119b9ad697748293c4bb1408853">tf::Executor::task_group</a></div><div class="ttdeci">TaskGroup task_group()</div><div class="ttdoc">creates a task group that executes a collection of asynchronous tasks</div><div class="ttdef"><b>Definition</b> task_group.hpp:875</div></div>
<div class="ttc" id="aclasstf_1_1Executor_html_af960048056f7c6b5bc71f4f526f05df7"><div class="ttname"><a href="classtf_1_1Executor.html#af960048056f7c6b5bc71f4f526f05df7">tf::Executor::async</a></div><div class="ttdeci">auto async(P &&params, F &&func)</div><div class="ttdoc">creates a parameterized asynchronous task to run the given function</div></div>
<div class="ttc" id="aclasstf_1_1TaskGroup_html"><div class="ttname"><a href="classtf_1_1TaskGroup.html">tf::TaskGroup</a></div><div class="ttdoc">class to create a task group from a task</div><div class="ttdef"><b>Definition</b> task_group.hpp:61</div></div>
<div class="ttc" id="aclasstf_1_1TaskGroup_html_a1f481dc466e3107a08346d1a124677bc"><div class="ttname"><a href="classtf_1_1TaskGroup.html#a1f481dc466e3107a08346d1a124677bc">tf::TaskGroup::corun</a></div><div class="ttdeci">void corun()</div><div class="ttdoc">corun all tasks spawned by this task group with other workers</div><div class="ttdef"><b>Definition</b> task_group.hpp:725</div></div>
<div class="ttc" id="aclasstf_1_1TaskGroup_html_acf90acfcaf9468adc56bf647208a9e78"><div class="ttname"><a href="classtf_1_1TaskGroup.html#acf90acfcaf9468adc56bf647208a9e78">tf::TaskGroup::silent_async</a></div><div class="ttdeci">void silent_async(F &&f)</div><div class="ttdoc">runs the given function asynchronously without returning any future object</div><div class="ttdef"><b>Definition</b> task_group.hpp:756</div></div>
</div><!-- fragment --><dl class="section note"><dt>Note</dt><dd>Prefer <a class="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> when you are already inside a runtime task, as it avoids the construction overhead of a task group object. Use <a class="el" href="classtf_1_1TaskGroup.html" title="class to create a task group from a task">tf::TaskGroup</a> when the recursive function is called from a context that does not hold a <a class="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> reference. </dd></dl>
</div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<!-- HTML footer for doxygen 1.13.1-->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="navelem"><a class="el" href="Examples.html">Learning from Examples</a></li>
<li class="footer">
Maintained by <a href="https://tsung-wei-huang.github.io/">Dr. Tsung-Wei Huang</a>
—
Generated by <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.13.1
</li>
</ul>
</div>