Taskflow  2.4-master-branch
observer.hpp
1 // 2020/04/30 - midified by Tsung-Wei Huang
2 // - adding TaskflowBoard support
3 //
4 // 2019/07/31 - modified by Tsung-Wei Huang
5 // - fixed the missing comma in outputing JSON
6 //
7 // 2019/06/13 - modified by Tsung-Wei Huang
8 // - added TaskView interface
9 //
10 // 2019/04/17 - created by Tsung-Wei Huang
11 
12 #pragma once
13 
14 #include "task.hpp"
15 
16 namespace tf {
17 
28 
29  friend class Executor;
30 
31  public:
32 
36  virtual ~ObserverInterface() = default;
37 
42  virtual void set_up(size_t num_workers) = 0;
43 
49  virtual void on_entry(size_t worker_id, TaskView task_view) = 0;
50 
56  virtual void on_exit(size_t worker_id, TaskView task_view) = 0;
57 };
58 
59 // ----------------------------------------------------------------------------
60 // ChromeTracingObserver definition
61 // ----------------------------------------------------------------------------
62 
70 
71  friend class Executor;
72 
73  // data structure to record each task execution
74  struct Segment {
75 
76  std::string name;
77 
80 
81  Segment(
82  const std::string& n,
84  );
85 
86  Segment(
87  const std::string& n,
90  );
91  };
92 
93  // data structure to store the entire execution timeline
94  struct Timeline {
97  };
98 
99  public:
100 
105  inline void dump(std::ostream& ostream) const;
106 
111  inline std::string dump() const;
112 
116  inline void clear();
117 
122  inline size_t num_tasks() const;
123 
124  private:
125 
126  inline void set_up(size_t num_workers) override final;
127  inline void on_entry(size_t worker_id, TaskView task_view) override final;
128  inline void on_exit(size_t worker_id, TaskView task_view) override final;
129 
130  Timeline _timeline;
131 };
132 
133 // constructor
134 inline ChromeTracingObserver::Segment::Segment(
135  const std::string& n,
137 ) :
138  name {n}, beg {b} {
139 }
140 
141 // constructor
142 inline ChromeTracingObserver::Segment::Segment(
143  const std::string& n,
146 ) :
147  name {n}, beg {b}, end {e} {
148 }
149 
150 // Procedure: set_up
151 inline void ChromeTracingObserver::set_up(size_t num_workers) {
152 
153  _timeline.segments.resize(num_workers);
154 
155  for(size_t w=0; w<num_workers; ++w) {
156  _timeline.segments[w].reserve(32);
157  }
158 
159  _timeline.origin = std::chrono::steady_clock::now();
160 }
161 
162 // Procedure: on_entry
163 inline void ChromeTracingObserver::on_entry(size_t w, TaskView tv) {
164  _timeline.segments[w].emplace_back(
165  tv.name(), std::chrono::steady_clock::now()
166  );
167 }
168 
169 // Procedure: on_exit
170 inline void ChromeTracingObserver::on_exit(size_t w, TaskView) {
171  assert(_timeline.segments[w].size() > 0);
172  _timeline.segments[w].back().end = std::chrono::steady_clock::now();
173 }
174 
175 // Function: clear
177  for(size_t w=0; w<_timeline.segments.size(); ++w) {
178  _timeline.segments[w].clear();
179  }
180 }
181 
182 // Procedure: dump
184 
185  size_t first;
186 
187  for(first = 0; first<_timeline.segments.size(); ++first) {
188  if(_timeline.segments[first].size() > 0) {
189  break;
190  }
191  }
192 
193  os << '[';
194 
195  for(size_t w=first; w<_timeline.segments.size(); w++) {
196 
197  if(w != first && _timeline.segments[w].size() > 0) {
198  os << ',';
199  }
200 
201  for(size_t i=0; i<_timeline.segments[w].size(); i++) {
202 
203  os << '{'
204  << "\"cat\":\"ChromeTracingObserver\",";
205 
206  // name field
207  os << "\"name\":\"";
208  if(_timeline.segments[w][i].name.empty()) {
209  os << w << '_' << i;
210  }
211  else {
212  os << _timeline.segments[w][i].name;
213  }
214  os << "\",";
215 
216  // segment field
217  os << "\"ph\":\"X\","
218  << "\"pid\":1,"
219  << "\"tid\":" << w << ','
221  _timeline.segments[w][i].beg - _timeline.origin
222  ).count() << ','
224  _timeline.segments[w][i].end - _timeline.segments[w][i].beg
225  ).count();
226 
227  if(i != _timeline.segments[w].size() - 1) {
228  os << "},";
229  }
230  else {
231  os << '}';
232  }
233  }
234  }
235  os << "]\n";
236 }
237 
238 // Function: dump
240  std::ostringstream oss;
241  dump(oss);
242  return oss.str();
243 }
244 
245 // Function: num_tasks
246 inline size_t ChromeTracingObserver::num_tasks() const {
247  return std::accumulate(
248  _timeline.segments.begin(), _timeline.segments.end(), size_t{0},
249  [](size_t sum, const auto& exe){
250  return sum + exe.size();
251  }
252  );
253 }
254 
255 // ----------------------------------------------------------------------------
256 // TFProfObserver definition
257 // ----------------------------------------------------------------------------
258 
266 
267  friend class Executor;
268 
269  // data structure to record each task execution
270  struct Segment {
271 
272  std::string name;
273  TaskType type;
274 
277 
278  Segment(
279  const std::string& n,
280  TaskType t,
282  );
283 
284  Segment(
285  const std::string& n,
286  TaskType t,
289  );
290  };
291 
292  // data structure to store the entire execution timeline
293  struct Timeline {
296  };
297 
298  public:
299 
304  inline void dump(std::ostream& ostream) const;
305 
310  inline std::string dump() const;
311 
315  inline void clear();
316 
321  inline size_t num_tasks() const;
322 
323  private:
324 
325  inline void set_up(size_t num_workers) override final;
326  inline void on_entry(size_t worker_id, TaskView task_view) override final;
327  inline void on_exit(size_t worker_id, TaskView task_view) override final;
328 
329  Timeline _timeline;
330 
331  UUID _uuid;
332 };
333 
334 // constructor
335 inline TFProfObserver::Segment::Segment(
336  const std::string& n,
337  TaskType t,
339 ) :
340  name {n}, type {t}, beg {b} {
341 }
342 
343 // constructor
344 inline TFProfObserver::Segment::Segment(
345  const std::string& n,
346  TaskType t,
349 ) :
350  name {n}, type {t}, beg {b}, end {e} {
351 }
352 
353 // Procedure: set_up
354 inline void TFProfObserver::set_up(size_t num_workers) {
355 
356  _timeline.segments.resize(num_workers);
357 
358  for(size_t w=0; w<num_workers; ++w) {
359  _timeline.segments[w].reserve(32);
360  }
361 
362  _timeline.origin = std::chrono::steady_clock::now();
363 }
364 
365 // Procedure: on_entry
366 inline void TFProfObserver::on_entry(size_t w, TaskView tv) {
367  _timeline.segments[w].emplace_back(
368  tv.name(), tv.type(), std::chrono::steady_clock::now()
369  );
370 }
371 
372 // Procedure: on_exit
373 inline void TFProfObserver::on_exit(size_t w, TaskView) {
374  assert(_timeline.segments[w].size() > 0);
375  _timeline.segments[w].back().end = std::chrono::steady_clock::now();
376 }
377 
378 // Function: clear
379 inline void TFProfObserver::clear() {
380  for(size_t w=0; w<_timeline.segments.size(); ++w) {
381  _timeline.segments[w].clear();
382  }
383 }
384 
385 // Procedure: dump
386 inline void TFProfObserver::dump(std::ostream& os) const {
387 
388  size_t first;
389 
390  for(first = 0; first<_timeline.segments.size(); ++first) {
391  if(_timeline.segments[first].size() > 0) {
392  break;
393  }
394  }
395 
396  // not timeline data to dump
397  if(first == _timeline.segments.size()) {
398  os << "{}\n";
399  return;
400  }
401 
402  os << "{\"executor\":\"" << _uuid << "\",\"data\":[";
403 
404  for(size_t w=first; w<_timeline.segments.size(); w++) {
405 
406  if(_timeline.segments[w].empty()) {
407  continue;
408  }
409 
410  if(w != first) {
411  os << ',';
412  }
413 
414  os << "{\"worker\":\"worker " << w << "\",\"data\":[";
415  for(size_t i=0; i<_timeline.segments[w].size(); ++i) {
416 
417  const auto& s = _timeline.segments[w][i];
418 
419  if(i) os << ',';
420 
421  // span
422  os << "{\"span\":["
424  s.beg - _timeline.origin
425  ).count() << ","
427  s.end - _timeline.origin
428  ).count() << "],";
429 
430  // name
431  os << "\"name\":\"";
432  if(s.name.empty()) {
433  os << w << '_' << i;
434  }
435  else {
436  os << s.name;
437  }
438  os << "\",";
439 
440  // category "type": "Condition Task",
441  os << "\"type\":\"" << task_type_to_string(s.type) << "\"";
442 
443  os << "}";
444  }
445  os << "]}";
446  }
447 
448  os << "]}\n";
449 }
450 
451 // Function: dump
453  std::ostringstream oss;
454  dump(oss);
455  return oss.str();
456 }
457 
458 // Function: num_tasks
459 inline size_t TFProfObserver::num_tasks() const {
460  return std::accumulate(
461  _timeline.segments.begin(), _timeline.segments.end(), size_t{0},
462  [](size_t sum, const auto& exe){
463  return sum + exe.size();
464  }
465  );
466 }
467 
468 // ----------------------------------------------------------------------------
469 // Identifier for Each Built-in Observer
470 // ----------------------------------------------------------------------------
471 
477 enum ObserverType {
478  TFPROF = 1,
479  CHROME = 2
480 };
481 
485 inline const char* observer_type_to_string(ObserverType type) {
486  const char* val;
487  switch(type) {
488  case TFPROF: val = "TFProf"; break;
489  case CHROME: val = "Chrome"; break;
490  default: val = "undefined"; break;
491  }
492  return val;
493 }
494 
495 // ----------------------------------------------------------------------------
496 // Legacy Alias
497 // ----------------------------------------------------------------------------
498 using ExecutorObserverInterface = ObserverInterface;
499 using ExecutorObserver = ChromeTracingObserver;
500 
501 
502 } // end of namespace tf -----------------------------------------------------
503 
504 
size_t num_tasks() const
get the number of total tasks in the observer
Definition: observer.hpp:246
void clear()
clear the timeline data
Definition: observer.hpp:379
virtual void on_entry(size_t worker_id, TaskView task_view)=0
method to call before a worker thread executes a closure
std::string dump() const
dump the timelines in JSON to a std::string
Definition: observer.hpp:452
T duration_cast(T... args)
T end(T... args)
Definition: error.hpp:9
observer designed based on taskflow board format
Definition: observer.hpp:265
virtual void on_exit(size_t worker_id, TaskView task_view)=0
method to call after a worker thread executed a closure
virtual void set_up(size_t num_workers)=0
constructor-like method to call when the executor observer is fully created
class to access task information from the observer interface
Definition: task.hpp:529
observer designed based on chrome tracing format
Definition: observer.hpp:69
std::string dump() const
dump the timelines in JSON to a std::string
Definition: observer.hpp:239
void clear()
clear the timeline data
Definition: observer.hpp:176
size_t num_tasks() const
get the number of total tasks in the observer
Definition: observer.hpp:459
execution interface for running a taskflow graph
Definition: executor.hpp:43
The interface class for creating an executor observer.
Definition: observer.hpp:27
virtual ~ObserverInterface()=default
virtual destructor