Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 56106af

Browse files
Merge branch 'dev'
2 parents fef4af0 + ff4f9ed commit 56106af

32 files changed

+218
-55
lines changed

README.md

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,10 @@ to enable high performance and high developer productivity at the same time.
3838

3939
![](image/framework.png)
4040

41-
Cpp-Taskflow provides visualization of the underlying executor's activities to help users analyze their program's performance.
41+
Cpp-Taskflow let users easily monitor the thread activities and analyze their programs' performance through chrome://tracing.
4242

4343
![](image/timeline.png)
4444

45-
46-
4745
Cpp-Taskflow is committed to support both academic and industry research projects,
4846
making it reliable and cost-effective for long-term and large-scale developments.
4947

@@ -69,6 +67,7 @@ visit the [documentation][wiki] to learn more about Cpp-Taskflow.
6967
* [Step 2: Execute a Framework](#step-2-execute-a-framework)
7068
* [Step 3: Framework Composition](#step-3-framework-composition)
7169
* [Debug a Taskflow Graph](#debug-a-taskflow-graph)
70+
* [Monitor Thread Activities](#monitor-thread-activities)
7271
* [API Reference](#api-reference)
7372
* [Caveats](#caveats)
7473
* [System Requirements](#system-requirements)
@@ -633,6 +632,43 @@ f2.name("f2");
633632
f2.dump(std::cout); // dump the framework
634633
```
635634

635+
# Monitor Thread Activities
636+
637+
Understanding thread activities is very important for performance analysis. Cpp-Taskflow provides a default *observer* of type `tf::ExecutorObserver` to let users observe when a thread starts or stops participating in task scheduling.
638+
639+
```cpp
640+
tf::Taskflow taskflow;
641+
// Create an observer
642+
auto observer = taskflow.share_executor()->make_observer<tf::ExecutorObserver>();
643+
```
644+
645+
When you dispatch a task dependency graph,
646+
the observer will automatically record the start and end timestamps of each executed task.
647+
You can dump the entire execution timelines into a JSON file.
648+
649+
```cpp
650+
tf::Taskflow taskflow;
651+
// Create an observer
652+
auto observer = taskflow.share_executor()->make_observer<tf::ExecutorObserver>();
653+
654+
// Add tasks ....
655+
656+
// Dispatch the tasks to execution
657+
taskflow.wait_for_all();
658+
659+
// Dump the timestamps to a JSON file
660+
std::ofstream ofs("timestamps.json");
661+
observer->dump(ofs);
662+
```
663+
664+
You can open the chrome browser to visualize the execution timelines through the chrome://tracing developer tool. In the tracing view, click the `Load` button to read the JSON file.
665+
You shall see the tracing graph.
666+
667+
![](image/timeline.png)
668+
669+
Each task is given a name of `i_j` where `i` is the thread id and `j` is the task number.
670+
You can pan or zoom in/out the timeline to get into a detailed view.
671+
636672
# API Reference
637673
638674
The official [documentation][wiki] explains the complete list of
@@ -1042,5 +1078,5 @@ Cpp-Taskflow is licensed under the [MIT License](./LICENSE).
10421078
[Shiva]: https://shiva.gitbook.io/project/shiva
10431079

10441080
[Presentation]: https://cpp-taskflow.github.io/
1045-
1081+
[chrome://tracing]: chrome://tracing
10461082

docs/Cookbook.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ var Cookbook =
4848
[ "Share an Executor among Taskflow Objects", "chapter6.html#C6_ShareAnExecutorAmongTaskflowObjects", null ],
4949
[ "Customize Your Executor Interface", "chapter6.html#C6CustomizeYourExecutorInterface", null ],
5050
[ "Thread Safety", "chapter6.html#C6ThreadSafety", null ],
51-
[ "Example 1: Impact of Over-subscription", "chapter6.html#C6Example1", null ]
51+
[ "Example 1: Impact of Over-subscription", "chapter6.html#C6Example1", null ],
52+
[ "Monitor Thread Activities", "chapter6.html#C6MonitorThreadActivities", null ]
5253
] ],
5354
[ "C7: Framework (Experimental)", "chapter7.html", [
5455
[ "Create a Framework", "chapter7.html#C7_CreateAFramework", null ],

docs/chapter6.html

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ <h1><a class="anchor" id="C6_MasterWorkersAndExecutor"></a>
108108
<div class="fragment"><div class="line"><span class="comment">// create 100 taskflow objects on top of the same executor</span></div><div class="line"><a class="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/container/list.html">std::list&lt;tf::Taskflow&gt;</a> tfs;</div><div class="line"><span class="keyword">auto</span> executor = std::make_shared&lt;tf::Taskflow::Executor&gt;(4);</div><div class="line"><span class="keywordflow">for</span>(<span class="keywordtype">size_t</span> i=0; i&lt;100; ++i) {</div><div class="line"> assert(executor.use_count() == i + 1); <span class="comment">// by the executor and each taskflow</span></div><div class="line"> tfs.emplace_back(executor); <span class="comment">// create a taskflow object from the executor</span></div><div class="line">}</div><div class="line"><span class="comment">// a total of 1 + 4 = 5 threads running in this program</span></div></div><!-- fragment --><h1><a class="anchor" id="C6CustomizeYourExecutorInterface"></a>
109109
Customize Your Executor Interface</h1>
110110
<p>Cpp-Taskflow permits users to define their own executor interface and integrate it into the taskflow object being built. In most cases, the executor is implemented as a thread pool to run given tasks. Your executor class must obey the following concepts in order to work with Cpp-Taskflow:</p>
111-
<div class="fragment"><div class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> C&gt;</div><div class="line"><span class="keyword">class </span>MyExecutor { <span class="comment">// closure type C, callable on operator ()</span></div><div class="line"></div><div class="line"> <span class="keyword">public</span>:</div><div class="line"></div><div class="line"> MyExecutor(<span class="keywordtype">unsigned</span>); <span class="comment">// constructor on a number of worker threads (might be zero)</span></div><div class="line"> <span class="keywordtype">size_t</span> num_workers() <span class="keyword">const</span>; <span class="comment">// return the number of worker threads (might be zero)</span></div><div class="line"> <span class="keyword">template</span> &lt;<span class="keyword">typename</span>... ArgsT&gt;</div><div class="line"> <span class="keywordtype">void</span> emplace(ArgsT&amp;&amp;...); <span class="comment">// arguments to construct the closure C</span></div><div class="line"> <span class="keyword">template</span> &lt;<span class="keyword">typename</span> C&gt;</div><div class="line"> <span class="keywordtype">void</span> batch(<a class="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/container/vector.html">std::vector&lt;C&gt;</a>&amp;); <span class="comment">// a vector of closures for batch insertions</span></div><div class="line">};</div><div class="line"><span class="keyword">using</span> MyTaskflow = <a class="code" href="classtf_1_1BasicTaskflow.html">tf::BasicTaskflow&lt;MyExecutor&gt;</a>;</div></div><!-- fragment --><p>The executor class template with one parameter on the task type. The task type can be a generic polymorphic function wrapper, for instance, <code>std::function&lt;void()&gt;</code>, or a callable class with fixed memory layout. It is completely up to users to define how to invoke the task. Your executor class must meet the following concepts:</p>
111+
<div class="fragment"><div class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> C&gt;</div><div class="line"><span class="keyword">class </span>MyExecutor { <span class="comment">// closure type C, callable on operator ()</span></div><div class="line"></div><div class="line"> <span class="keyword">public</span>:</div><div class="line"></div><div class="line"> MyExecutor(<span class="keywordtype">unsigned</span>); <span class="comment">// constructor on a number of worker threads (might be zero)</span></div><div class="line"></div><div class="line"> <span class="keywordtype">size_t</span> num_workers() <span class="keyword">const</span>; <span class="comment">// return the number of worker threads (might be zero)</span></div><div class="line"></div><div class="line"> <span class="keyword">template</span> &lt;<span class="keyword">typename</span>... ArgsT&gt;</div><div class="line"> <span class="keywordtype">void</span> emplace(ArgsT&amp;&amp;...); <span class="comment">// arguments to construct the closure C</span></div><div class="line"></div><div class="line"> <span class="keyword">template</span> &lt;<span class="keyword">typename</span> C&gt;</div><div class="line"> <span class="keywordtype">void</span> batch(<a class="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/container/vector.html">std::vector&lt;C&gt;</a>&amp;); <span class="comment">// a vector of closures for batch insertions</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">using</span> MyTaskflow = <a class="code" href="classtf_1_1BasicTaskflow.html">tf::BasicTaskflow&lt;MyExecutor&gt;</a>;</div></div><!-- fragment --><p>The executor class template with one parameter on the task type. The task type can be a generic polymorphic function wrapper, for instance, <code>std::function&lt;void()&gt;</code>, or a callable class with fixed memory layout. It is completely up to users to define how to invoke the task. Your executor class must meet the following concepts:</p>
112112
<ul>
113113
<li>a constructor on a given number of worker threads </li>
114114
<li>a constant method num_workers to return the number of worker threads </li>
@@ -129,7 +129,29 @@ <h1><a class="anchor" id="C6Example1"></a>
129129
<li>Line 42-68 creates multiple taskflow objects from the same executor to run on the same set of threads</li>
130130
</ul>
131131
<p>Running the program on different number of taskflow objects gives the following runtime values:</p>
132-
<div class="fragment"><div class="line"># taskflows shared (ms) unique (ms)</div><div class="line"> 1 120 114</div><div class="line"> 2 225 229</div><div class="line"> 4 451 452</div><div class="line"> 8 908 904</div><div class="line"> 16 1791 1837</div><div class="line"> 32 3581 3782</div><div class="line"> 64 7183 7636</div><div class="line"> 128 14341 15482</div></div><!-- fragment --><p>As we increase the number of taskflow objects, the implementation without sharing the executor encounters more context switches among threads. This overhead reflected on the slower runtime (15482 vs 14341 on 128 taskflow objects). </p>
132+
<div class="fragment"><div class="line"># taskflows shared (ms) unique (ms)</div><div class="line"> 1 120 114</div><div class="line"> 2 225 229</div><div class="line"> 4 451 452</div><div class="line"> 8 908 904</div><div class="line"> 16 1791 1837</div><div class="line"> 32 3581 3782</div><div class="line"> 64 7183 7636</div><div class="line"> 128 14341 15482</div></div><!-- fragment --><p>As we increase the number of taskflow objects, the implementation without sharing the executor encounters more context switches among threads. This overhead reflected on the slower runtime (15482 vs 14341 on 128 taskflow objects).</p>
133+
<h1><a class="anchor" id="C6MonitorThreadActivities"></a>
134+
Monitor Thread Activities</h1>
135+
<p>Inspecting the thread activities is very important for performance analysis. It allows you to know when each task starts and ends participating in the task scheduling. Cpp-Taskflow provides a default observer class <a class="el" href="classtf_1_1ExecutorObserver.html" title="A default executor observer to dump the execution timelines. ">tf::ExecutorObserver</a> for this purpose. The following example shows how to create an observer from a taskflow.</p>
136+
<div class="fragment"><div class="line"><a class="code" href="classtf_1_1BasicTaskflow.html">tf::Taskflow</a> taskflow;</div><div class="line"><span class="keyword">auto</span> observer = taskflow.<a class="code" href="classtf_1_1BasicTaskflow.html#abe76e5288016861aaf1dafc0218d3084">share_executor</a>()-&gt;make_observer&lt;<a class="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>&gt;();</div></div><!-- fragment --><p>Note that each executor can only have an observer at a time. An observer will automatically record the start and end timestamps of each executed task. Users can query, dump or remove the timestamps through the <a class="el" href="classtf_1_1ExecutorObserver.html#a2cfd00ec287c7574e515a588a42b3be4" title="get the number of total tasks in the observer ">tf::ExecutorObserver::num_tasks</a>, <a class="el" href="classtf_1_1ExecutorObserver.html#a20f77a06e0f10dc67f7a5742add5b00a" title="dump the timelines in JSON format to an ostream ">tf::ExecutorObserver::dump</a> and <a class="el" href="classtf_1_1ExecutorObserver.html#adc78a004eaa25022a20fd16a35f607ce" title="clear the timeline data ">tf::ExecutorObserver::clear</a> methods.</p>
137+
<div class="fragment"><div class="line"> 1. <a class="code" href="classtf_1_1BasicTaskflow.html">tf::Taskflow</a> taskflow;</div><div class="line"> 2. <span class="keyword">auto</span> observer = taskflow.<a class="code" href="classtf_1_1BasicTaskflow.html#abe76e5288016861aaf1dafc0218d3084">share_executor</a>()-&gt;make_observer&lt;<a class="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>&gt;();</div><div class="line"> 3. taskflow.<a class="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([](){}, [](){}, [](){}, [](){});</div><div class="line"> 4. taskflow.<a class="code" href="classtf_1_1BasicTaskflow.html#a37ef86998f23ee7315be032c40fe815e">wait_for_all</a>();</div><div class="line"> 5.</div><div class="line"> 6. <span class="comment">// Query the total number of tasks (number of timestamp pairs)</span></div><div class="line"> 7. <span class="keyword">auto</span> num_tasks = observer-&gt;num_tasks();</div><div class="line"> 8.</div><div class="line"> 9. <span class="comment">// Dump the timeline data in JSON format </span></div><div class="line">10. <a class="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/string/basic_string.html">std::string</a> timelines = observer-&gt;dump();</div><div class="line">11. </div><div class="line">12. <span class="comment">// Clear the timeline data</span></div><div class="line">13. observer-&gt;clear();</div></div><!-- fragment --><p>Debrief:</p>
138+
<ul>
139+
<li>Line 2-4 creates an observer and a task dependency graph with four tasks and dispatch the tasks to execution. </li>
140+
<li>Line 7 query the total number of tasks (number of timestamp pair) through observer </li>
141+
<li>Line 10 dump the timestamps to a <a class="elRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/docs/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/string/basic_string.html">std::string</a> in JSON format </li>
142+
<li>Line 13 remove all timestamps in the observer</li>
143+
</ul>
144+
<p>You can visualize the timeline data in a Chrome browser:</p>
145+
<ul>
146+
<li>Step 1: save the JSON timeline data to a file </li>
147+
<li>Step 2: launch the Chrome browser and open a tab with the url: chrome://tracing </li>
148+
<li>Step 3: load the JSON file</li>
149+
</ul>
150+
<div class="image">
151+
<img src="timeline.png" alt="timeline.png" width="80%"/>
152+
</div>
153+
<p>Tasks will be categorized by the executing thread and each task is named with <em>i_j</em> where <em>i</em> is the thread id and <em>j</em> is the task number. You can pan or zoom in/out the timeline to get a detailed view.</p>
154+
<p>You can derive your own observer from the base interface class <a class="el" href="classtf_1_1ExecutorObserverInterface.html" title="The interface class for creating an executor observer. ">tf::ExecutorObserverInterface</a> to customize the observing methods. </p>
133155
</div></div><!-- contents -->
134156
</div><!-- doc-content -->
135157
<!-- start footer part -->

docs/classtf_1_1ExecutorObserver-members.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@
101101
<tr class="even"><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html#adc78a004eaa25022a20fd16a35f607ce">clear</a>()</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a></td><td class="entry"><span class="mlabel">inline</span></td></tr>
102102
<tr><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html#a20f77a06e0f10dc67f7a5742add5b00a">dump</a>(std::ostream &amp;ostream) const</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a></td><td class="entry"><span class="mlabel">inline</span></td></tr>
103103
<tr class="even"><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html#af1207fd394fa292f657a8ed4f0891858">dump</a>() const</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a></td><td class="entry"><span class="mlabel">inline</span></td></tr>
104-
<tr><td class="entry"><a class="el" href="classtf_1_1ExecutorObserverInterface.html#a239e48ee46d73bcd5a3f28b3e1b8fe50">~ExecutorObserverInterface</a>()=default</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserverInterface.html">tf::ExecutorObserverInterface</a></td><td class="entry"><span class="mlabel">virtual</span></td></tr>
104+
<tr><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html#a2cfd00ec287c7574e515a588a42b3be4">num_tasks</a>() const</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a></td><td class="entry"><span class="mlabel">inline</span></td></tr>
105+
<tr class="even"><td class="entry"><a class="el" href="classtf_1_1ExecutorObserverInterface.html#a239e48ee46d73bcd5a3f28b3e1b8fe50">~ExecutorObserverInterface</a>()=default</td><td class="entry"><a class="el" href="classtf_1_1ExecutorObserverInterface.html">tf::ExecutorObserverInterface</a></td><td class="entry"><span class="mlabel">virtual</span></td></tr>
105106
</table></div><!-- contents -->
106107
</div><!-- doc-content -->
107108
<!-- start footer part -->

0 commit comments

Comments
 (0)