Loading...
Searching...
No Matches
observer.hpp
1#pragma once
2
3#include "task.hpp"
4#include "worker.hpp"
5
10
11namespace tf {
12
13// ----------------------------------------------------------------------------
14// timeline data structure
15// ----------------------------------------------------------------------------
16
20using observer_stamp_t = std::chrono::time_point<std::chrono::steady_clock>;
21
25struct Segment {
26
27 std::string name;
28 TaskType type;
29
32
33 template <typename Archiver>
34 auto save(Archiver& ar) const {
35 return ar(name, type, beg, end);
36 }
37
38 template <typename Archiver>
39 auto load(Archiver& ar) {
40 return ar(name, type, beg, end);
41 }
42
43 Segment() = default;
44
45 Segment(
46 const std::string& n, TaskType t, observer_stamp_t b, observer_stamp_t e
47 ) : name {n}, type {t}, beg {b}, end {e} {
48 }
49
50 auto span() const {
51 return end-beg;
52 }
53};
54
58struct Timeline {
59
60 size_t uid;
61
62 observer_stamp_t origin;
63 std::vector<std::vector<std::vector<Segment>>> segments;
64
65 Timeline() = default;
66
67 Timeline(const Timeline& rhs) = delete;
68 Timeline(Timeline&& rhs) = default;
69
70 Timeline& operator = (const Timeline& rhs) = delete;
71 Timeline& operator = (Timeline&& rhs) = default;
72
73 template <typename Archiver>
74 auto save(Archiver& ar) const {
75 return ar(uid, origin, segments);
76 }
77
78 template <typename Archiver>
79 auto load(Archiver& ar) {
80 return ar(uid, origin, segments);
81 }
82};
83
87struct ProfileData {
88
89 std::vector<Timeline> timelines;
90
91 ProfileData() = default;
92
93 ProfileData(const ProfileData& rhs) = delete;
94 ProfileData(ProfileData&& rhs) = default;
95
96 ProfileData& operator = (const ProfileData& rhs) = delete;
97 ProfileData& operator = (ProfileData&&) = default;
98
99 template <typename Archiver>
100 auto save(Archiver& ar) const {
101 return ar(timelines);
102 }
103
104 template <typename Archiver>
105 auto load(Archiver& ar) {
106 return ar(timelines);
107 }
108};
109
110// ----------------------------------------------------------------------------
111// observer interface
112// ----------------------------------------------------------------------------
113
170
171 public:
172
176 virtual ~ObserverInterface() = default;
177
182 virtual void set_up(size_t num_workers) = 0;
183
189 virtual void on_entry(WorkerView wv, TaskView task_view) = 0;
190
196 virtual void on_exit(WorkerView wv, TaskView task_view) = 0;
197};
198
199// ----------------------------------------------------------------------------
200// ChromeObserver definition
201// ----------------------------------------------------------------------------
202
230
231 friend class Executor;
232
233 // data structure to record each task execution
234 struct Segment {
235
236 std::string name;
237
240
241 Segment(
242 const std::string& n,
245 );
246 };
247
248 // data structure to store the entire execution timeline
249 struct Timeline {
250 observer_stamp_t origin;
251 std::vector<std::vector<Segment>> segments;
252 std::vector<std::stack<observer_stamp_t>> stacks;
253 };
254
255 public:
256
261 void dump(std::ostream& ostream) const;
262
266 inline std::string dump() const;
267
271 inline void clear();
272
276 inline size_t num_tasks() const;
277
278 private:
279
280 inline void set_up(size_t num_workers) override final;
281 inline void on_entry(WorkerView w, TaskView task_view) override final;
282 inline void on_exit(WorkerView w, TaskView task_view) override final;
283
284 Timeline _timeline;
285};
286
287// constructor
288inline ChromeObserver::Segment::Segment(
289 const std::string& n, observer_stamp_t b, observer_stamp_t e
290) :
291 name {n}, beg {b}, end {e} {
292}
293
294// Procedure: set_up
295inline void ChromeObserver::set_up(size_t num_workers) {
296 _timeline.segments.resize(num_workers);
297 _timeline.stacks.resize(num_workers);
298
299 for(size_t w=0; w<num_workers; ++w) {
300 _timeline.segments[w].reserve(32);
301 }
302
303 _timeline.origin = observer_stamp_t::clock::now();
304}
305
306// Procedure: on_entry
307inline void ChromeObserver::on_entry(WorkerView wv, TaskView) {
308 _timeline.stacks[wv.id()].push(observer_stamp_t::clock::now());
309}
310
311// Procedure: on_exit
312inline void ChromeObserver::on_exit(WorkerView wv, TaskView tv) {
313
314 size_t w = wv.id();
315
316 assert(!_timeline.stacks[w].empty());
317
318 auto beg = _timeline.stacks[w].top();
319 _timeline.stacks[w].pop();
320
321 _timeline.segments[w].emplace_back(
322 tv.name(), beg, observer_stamp_t::clock::now()
323 );
324}
325
326// Function: clear
328 for(size_t w=0; w<_timeline.segments.size(); ++w) {
329 _timeline.segments[w].clear();
330 while(!_timeline.stacks[w].empty()) {
331 _timeline.stacks[w].pop();
332 }
333 }
334}
335
336// Procedure: dump
337inline void ChromeObserver::dump(std::ostream& os) const {
338
339 using namespace std::chrono;
340
341 size_t first;
342
343 for(first = 0; first<_timeline.segments.size(); ++first) {
344 if(_timeline.segments[first].size() > 0) {
345 break;
346 }
347 }
348
349 os << '[';
350
351 for(size_t w=first; w<_timeline.segments.size(); w++) {
352
353 if(w != first && _timeline.segments[w].size() > 0) {
354 os << ',';
355 }
356
357 for(size_t i=0; i<_timeline.segments[w].size(); i++) {
358
359 os << '{'<< "\"cat\":\"ChromeObserver\",";
360
361 // name field
362 os << "\"name\":\"";
363 if(_timeline.segments[w][i].name.empty()) {
364 os << w << '_' << i;
365 }
366 else {
367 os << _timeline.segments[w][i].name;
368 }
369 os << "\",";
370
371 // segment field
372 os << "\"ph\":\"X\","
373 << "\"pid\":1,"
374 << "\"tid\":" << w << ','
375 << "\"ts\":" << duration_cast<microseconds>(
376 _timeline.segments[w][i].beg - _timeline.origin
377 ).count() << ','
378 << "\"dur\":" << duration_cast<microseconds>(
379 _timeline.segments[w][i].end - _timeline.segments[w][i].beg
380 ).count();
381
382 if(i != _timeline.segments[w].size() - 1) {
383 os << "},";
384 }
385 else {
386 os << '}';
387 }
388 }
389 }
390 os << "]\n";
391}
392
393// Function: dump
394inline std::string ChromeObserver::dump() const {
395 std::ostringstream oss;
396 dump(oss);
397 return oss.str();
398}
399
400// Function: num_tasks
401inline size_t ChromeObserver::num_tasks() const {
402 return std::accumulate(
403 _timeline.segments.begin(), _timeline.segments.end(), size_t{0},
404 [](size_t sum, const auto& exe){
405 return sum + exe.size();
406 }
407 );
408}
409
410// ----------------------------------------------------------------------------
411// TFProfObserver definition
412// ----------------------------------------------------------------------------
413
442
443 friend class Executor;
444 friend class TFProfManager;
445
449 struct TaskSummary {
450 size_t count {0};
451 size_t total_span {0};
452 size_t min_span;
453 size_t max_span;
454
455 float avg_span() const { return total_span * 1.0f / count; }
456 };
457
461 struct WorkerSummary {
462
463 size_t id;
464 size_t level;
465 size_t count {0};
466 size_t total_span {0};
467 size_t min_span{0};
468 size_t max_span{0};
469
470 std::array<TaskSummary, TASK_TYPES.size()> tsum;
471
472 float avg_span() const { return total_span * 1.0f / count; }
473 //return count < 2 ? 0.0f : total_delay * 1.0f / (count-1);
474 };
475
479 struct Summary {
480 std::array<TaskSummary, TASK_TYPES.size()> tsum;
481 std::vector<WorkerSummary> wsum;
482
483 void dump_tsum(std::ostream&) const;
484 void dump_wsum(std::ostream&) const;
485 void dump(std::ostream&) const;
486 };
487
488 public:
489
494 void dump(std::ostream& ostream) const;
495
499 std::string dump() const;
500
504 void summary(std::ostream& ostream) const;
505
509 std::string summary() const;
510
514 void clear();
515
519 size_t num_tasks() const;
520
524 size_t num_workers() const;
525
526 private:
527
528 Timeline _timeline;
529
530 std::vector<std::stack<observer_stamp_t>> _stacks;
531
532 inline void set_up(size_t num_workers) override final;
533 inline void on_entry(WorkerView, TaskView) override final;
534 inline void on_exit(WorkerView, TaskView) override final;
535};
536
537
538// dump the task summary
539inline void TFProfObserver::Summary::dump_tsum(std::ostream& os) const {
540
541 // task summary
542 size_t type_w{10}, count_w{5}, time_w{9}, avg_w{8}, min_w{8}, max_w{8};
543
544 std::for_each(tsum.begin(), tsum.end(), [&](const auto& i){
545 if(i.count == 0) return;
546 count_w = (std::max)(count_w, std::to_string(i.count).size());
547 });
548
549 std::for_each(tsum.begin(), tsum.end(), [&](const auto& i){
550 if(i.count == 0) return;
551 time_w = (std::max)(time_w, std::to_string(i.total_span).size());
552 });
553
554 std::for_each(tsum.begin(), tsum.end(), [&](const auto& i){
555 if(i.count == 0) return;
556 avg_w = (std::max)(time_w, std::to_string(i.avg_span()).size());
557 });
558
559 std::for_each(tsum.begin(), tsum.end(), [&](const auto& i){
560 if(i.count == 0) return;
561 min_w = (std::max)(min_w, std::to_string(i.min_span).size());
562 });
563
564 std::for_each(tsum.begin(), tsum.end(), [&](const auto& i){
565 if(i.count == 0) return;
566 max_w = (std::max)(max_w, std::to_string(i.max_span).size());
567 });
568
569 os << std::setw(type_w) << "-Task-"
570 << std::setw(count_w+2) << "Count"
571 << std::setw(time_w+2) << "Time (us)"
572 << std::setw(avg_w+2) << "Avg (us)"
573 << std::setw(min_w+2) << "Min (us)"
574 << std::setw(max_w+2) << "Max (us)"
575 << '\n';
576
577 for(size_t i=0; i<TASK_TYPES.size(); i++) {
578 if(tsum[i].count == 0) {
579 continue;
580 }
581 os << std::setw(type_w) << to_string(TASK_TYPES[i])
582 << std::setw(count_w+2) << tsum[i].count
583 << std::setw(time_w+2) << tsum[i].total_span
584 << std::setw(avg_w+2) << std::to_string(tsum[i].avg_span())
585 << std::setw(min_w+2) << tsum[i].min_span
586 << std::setw(max_w+2) << tsum[i].max_span
587 << '\n';
588 }
589}
590
591// dump the worker summary
592inline void TFProfObserver::Summary::dump_wsum(std::ostream& os) const {
593
594 // task summary
595 size_t w_w{10}, t_w{10}, l_w{5}, c_w{5}, d_w{9}, avg_w{8}, min_w{8}, max_w{8};
596
597 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
598 if(i.count == 0) return;
599 l_w = (std::max)(l_w, std::to_string(i.level).size());
600 });
601
602 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
603 if(i.count == 0) return;
604 c_w = (std::max)(c_w, std::to_string(i.count).size());
605 });
606
607 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
608 if(i.count == 0) return;
609 d_w = (std::max)(d_w, std::to_string(i.total_span).size());
610 });
611
612 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
613 if(i.count == 0) return;
614 avg_w = (std::max)(avg_w, std::to_string(i.avg_span()).size());
615 });
616
617 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
618 if(i.count == 0) return;
619 min_w = (std::max)(min_w, std::to_string(i.min_span).size());
620 });
621
622 std::for_each(wsum.begin(), wsum.end(), [&](const auto& i){
623 if(i.count == 0) return;
624 max_w = (std::max)(max_w, std::to_string(i.max_span).size());
625 });
626
627 os << std::setw(w_w) << "-Worker-"
628 << std::setw(l_w+2) << "Level"
629 << std::setw(t_w) << "Task"
630 << std::setw(c_w+2) << "Count"
631 << std::setw(d_w+2) << "Time (us)"
632 << std::setw(avg_w+2) << "Avg (us)"
633 << std::setw(min_w+2) << "Min (us)"
634 << std::setw(max_w+2) << "Max (us)"
635 << '\n';
636
637 for(const auto& ws : wsum) {
638
639 if(ws.count == 0) {
640 continue;
641 }
642
643 os << std::setw(w_w) << ws.id
644 << std::setw(l_w+2) << ws.level;
645
646 bool first = true;
647 for(size_t i=0; i<TASK_TYPES.size(); i++) {
648
649 if(ws.tsum[i].count == 0) {
650 continue;
651 }
652
653 os << (first ? std::setw(t_w) : std::setw(w_w + l_w + 2 + t_w));
654 first = false;
655
656 os << to_string(TASK_TYPES[i])
657 << std::setw(c_w+2) << ws.tsum[i].count
658 << std::setw(d_w+2) << ws.tsum[i].total_span
659 << std::setw(avg_w+2) << std::to_string(ws.tsum[i].avg_span())
660 << std::setw(min_w+2) << ws.tsum[i].min_span
661 << std::setw(max_w+2) << ws.tsum[i].max_span
662 << '\n';
663 }
664
665 // per-worker summary
666 os << std::setw(w_w + l_w + t_w + c_w + 4) << ws.count
667 << std::setw(d_w+2) << ws.total_span
668 << std::setw(avg_w+2) << std::to_string(ws.avg_span())
669 << std::setw(min_w+2) << ws.min_span
670 << std::setw(max_w+2) << ws.max_span
671 << '\n';
672
673 //for(size_t j=0; j<w_w+l_w+t_w+4; j++) os << ' ';
674 //for(size_t j=0; j<c_w+d_w+avg_w+min_w+max_w+8; j++) os << '-';
675 //os <<'\n';
676 }
677}
678
679// dump the summary report through an ostream
680inline void TFProfObserver::Summary::dump(std::ostream& os) const {
681 dump_tsum(os);
682 os << '\n';
683 dump_wsum(os);
684}
685
686// Procedure: set_up
687inline void TFProfObserver::set_up(size_t num_workers) {
688 _timeline.uid = unique_id<size_t>();
689 _timeline.origin = observer_stamp_t::clock::now();
690 _timeline.segments.resize(num_workers);
691 _stacks.resize(num_workers);
692}
693
694// Procedure: on_entry
695inline void TFProfObserver::on_entry(WorkerView wv, TaskView) {
696 _stacks[wv.id()].push(observer_stamp_t::clock::now());
697}
698
699// Procedure: on_exit
700inline void TFProfObserver::on_exit(WorkerView wv, TaskView tv) {
701
702 size_t w = wv.id();
703
704 assert(!_stacks[w].empty());
705
706 if(_stacks[w].size() > _timeline.segments[w].size()) {
707 _timeline.segments[w].resize(_stacks[w].size());
708 }
709
710 auto beg = _stacks[w].top();
711 _stacks[w].pop();
712
713 _timeline.segments[w][_stacks[w].size()].emplace_back(
714 tv.name(), tv.type(), beg, observer_stamp_t::clock::now()
715 );
716}
717
718// Function: clear
720 for(size_t w=0; w<_timeline.segments.size(); ++w) {
721 for(size_t l=0; l<_timeline.segments[w].size(); ++l) {
722 _timeline.segments[w][l].clear();
723 }
724 while(!_stacks[w].empty()) {
725 _stacks[w].pop();
726 }
727 }
728}
729
730// Procedure: dump
731inline void TFProfObserver::dump(std::ostream& os) const {
732
733 using namespace std::chrono;
734
735 size_t first;
736
737 for(first = 0; first<_timeline.segments.size(); ++first) {
738 if(_timeline.segments[first].size() > 0) {
739 break;
740 }
741 }
742
743 // not timeline data to dump
744 if(first == _timeline.segments.size()) {
745 os << "{}\n";
746 return;
747 }
748
749 os << "{\"executor\":\"" << _timeline.uid << "\",\"data\":[";
750
751 bool comma = false;
752
753 for(size_t w=first; w<_timeline.segments.size(); w++) {
754 for(size_t l=0; l<_timeline.segments[w].size(); l++) {
755
756 if(_timeline.segments[w][l].empty()) {
757 continue;
758 }
759
760 if(comma) {
761 os << ',';
762 }
763 else {
764 comma = true;
765 }
766
767 os << "{\"worker\":" << w << ",\"level\":" << l << ",\"data\":[";
768 for(size_t i=0; i<_timeline.segments[w][l].size(); ++i) {
769
770 const auto& s = _timeline.segments[w][l][i];
771
772 if(i) os << ',';
773
774 // span
775 os << "{\"span\":["
776 << duration_cast<microseconds>(s.beg - _timeline.origin).count()
777 << ","
778 << duration_cast<microseconds>(s.end - _timeline.origin).count()
779 << "],";
780
781 // name
782 os << "\"name\":\"";
783 if(s.name.empty()) {
784 os << w << '_' << i;
785 }
786 else {
787 os << s.name;
788 }
789 os << "\",";
790
791 // e.g., category "type": "Condition Task"
792 os << "\"type\":\"" << to_string(s.type) << "\"";
793
794 os << "}";
795 }
796 os << "]}";
797 }
798 }
799
800 os << "]}\n";
801}
802
803// Function: dump
804inline std::string TFProfObserver::dump() const {
805 std::ostringstream oss;
806 dump(oss);
807 return oss.str();
808}
809
810// Procedure: summary
811inline void TFProfObserver::summary(std::ostream& os) const {
812
813 using namespace std::chrono;
814
815 Summary summary;
816 std::optional<observer_stamp_t> view_beg, view_end;
817
818 // find the first non-empty worker
819 size_t first;
820 for(first = 0; first<_timeline.segments.size(); ++first) {
821 if(_timeline.segments[first].size() > 0) {
822 break;
823 }
824 }
825
826 // not timeline data to dump
827 if(first == _timeline.segments.size()) {
828 goto end_of_summary;
829 }
830
831 for(size_t w=first; w<_timeline.segments.size(); w++) {
832 for(size_t l=0; l<_timeline.segments[w].size(); l++) {
833
834 if(_timeline.segments[w][l].empty()) {
835 continue;
836 }
837
838 // worker w at level l
839 WorkerSummary ws;
840 ws.id = w;
841 ws.level = l;
842 ws.count = _timeline.segments[w][l].size();
843
844 // scan all tasks at level l
845 for(size_t i=0; i<_timeline.segments[w][l].size(); ++i) {
846
847 // update the entire span
848 auto& s = _timeline.segments[w][l][i];
849 view_beg = view_beg ? (std::min)(*view_beg, s.beg) : s.beg;
850 view_end = view_end ? (std::max)(*view_end, s.end) : s.end;
851
852 // update the task summary
853 size_t t = duration_cast<microseconds>(s.end - s.beg).count();
854
855 auto& x = summary.tsum[static_cast<int>(s.type)];
856 x.count += 1;
857 x.total_span += t;
858 x.min_span = (x.count == 1) ? t : (std::min)(t, x.min_span);
859 x.max_span = (x.count == 1) ? t : (std::max)(t, x.max_span);
860
861 // update the worker summary
862 ws.total_span += t;
863 ws.min_span = (i == 0) ? t : (std::min)(t, ws.min_span);
864 ws.max_span = (i == 0) ? t : (std::max)(t, ws.max_span);
865
866 auto&y = ws.tsum[static_cast<int>(s.type)];
867 y.count += 1;
868 y.total_span += t;
869 y.min_span = (y.count == 1) ? t : (std::min)(t, y.min_span);
870 y.max_span = (y.count == 1) ? t : (std::max)(t, y.max_span);
871
872 // update the delay
873 //if(i) {
874 // size_t d = duration_cast<nanoseconds>(
875 // s.beg - _timeline.segments[w][l][i-1].end
876 // ).count();
877 // ws.total_delay += d;
878 // ws.min_delay = (i == 1) ? d : std::min(ws.min_delay, d);
879 // ws.max_delay = (i == 1) ? d : std::max(ws.max_delay, d);
880 //}
881 }
882 summary.wsum.push_back(ws);
883 }
884 }
885
886 end_of_summary:
887
888 size_t view = 0;
889 if(view_beg && view_end) {
890 view = duration_cast<microseconds>(*view_end - *view_beg).count();
891 }
892
893 os << "==Observer " << _timeline.uid << ": "
894 << num_workers() << " workers completed "
895 << num_tasks() << " tasks in "
896 << view << " us\n";
897
898 summary.dump(os);
899}
900
901// Procedure: summary
902inline std::string TFProfObserver::summary() const {
903 std::ostringstream oss;
904 summary(oss);
905 return oss.str();
906}
907
908// Function: num_tasks
909inline size_t TFProfObserver::num_tasks() const {
910 size_t s = 0;
911 for(size_t w=0; w<_timeline.segments.size(); ++w) {
912 for(size_t l=0; l<_timeline.segments[w].size(); ++l) {
913 s += _timeline.segments[w][l].size();
914 }
915 }
916 return s;
917}
918
919// Function: num_workers
920inline size_t TFProfObserver::num_workers() const {
921 size_t w = 0;
922 for(size_t i=0; i<_timeline.segments.size(); ++i) {
923 w += (!_timeline.segments[i].empty());
924 }
925 return w;
926}
927
928
929// ----------------------------------------------------------------------------
930// TFProfManager
931// ----------------------------------------------------------------------------
932
936class TFProfManager {
937
938 friend class Executor;
939
940 public:
941
942 ~TFProfManager();
943
944 TFProfManager(const TFProfManager&) = delete;
945 TFProfManager& operator=(const TFProfManager&) = delete;
946
947 static TFProfManager& get();
948
949 void dump(std::ostream& ostream) const;
950
951 private:
952
953 const std::string _fpath;
954
955 std::mutex _mutex;
956 std::vector<std::shared_ptr<TFProfObserver>> _observers;
957
958 TFProfManager();
959
960 void _manage(std::shared_ptr<TFProfObserver> observer);
961};
962
963// constructor
964inline TFProfManager::TFProfManager() :
965 _fpath {get_env(TF_ENABLE_PROFILER)} {
966
967}
968
969// Procedure: manage
970inline void TFProfManager::_manage(std::shared_ptr<TFProfObserver> observer) {
971 std::lock_guard lock(_mutex);
972 _observers.push_back(std::move(observer));
973}
974
975// Procedure: dump
976inline void TFProfManager::dump(std::ostream& os) const {
977 for(size_t i=0; i<_observers.size(); ++i) {
978 if(i) os << ',';
979 _observers[i]->dump(os);
980 }
981}
982
983// Destructor
984inline TFProfManager::~TFProfManager() {
985 std::ofstream ofs(_fpath);
986 if(ofs) {
987 // .tfp
988 if(_fpath.rfind(".tfp") != std::string::npos) {
989 ProfileData data;
990 data.timelines.reserve(_observers.size());
991 for(size_t i=0; i<_observers.size(); ++i) {
992 data.timelines.push_back(std::move(_observers[i]->_timeline));
993 }
994 Serializer<std::ofstream> serializer(ofs);
995 serializer(data);
996 }
997 // .json
998 else { // if(_fpath.rfind(".json") != std::string::npos) {
999 ofs << "[\n";
1000 for(size_t i=0; i<_observers.size(); ++i) {
1001 if(i) ofs << ',';
1002 _observers[i]->dump(ofs);
1003 }
1004 ofs << "]\n";
1005 }
1006 }
1007 // do a summary report in stderr for each observer
1008 else {
1009 std::ostringstream oss;
1010 for(size_t i=0; i<_observers.size(); ++i) {
1011 _observers[i]->summary(oss);
1012 }
1013 fprintf(stderr, "%s", oss.str().c_str());
1014 }
1015}
1016
1017// Function: get
1018inline TFProfManager& TFProfManager::get() {
1019 static TFProfManager mgr;
1020 return mgr;
1021}
1022
1023// ----------------------------------------------------------------------------
1024// Identifier for Each Built-in Observer
1025// ----------------------------------------------------------------------------
1026
1032enum class ObserverType : int {
1033 TFPROF = 0,
1034 CHROME,
1035 UNDEFINED
1036};
1037
1041inline const char* to_string(ObserverType type) {
1042 switch(type) {
1043 case ObserverType::TFPROF: return "tfprof";
1044 case ObserverType::CHROME: return "chrome";
1045 default: return "undefined";
1046 }
1047}
1048
1049
1050} // end of namespace tf -----------------------------------------------------
1051
1052
class to create an observer based on Chrome tracing format
Definition observer.hpp:229
void clear()
clears the timeline data
Definition observer.hpp:327
std::string dump() const
dumps the timelines into a Chrome Tracing format
Definition observer.hpp:394
size_t num_tasks() const
queries the number of tasks observed
Definition observer.hpp:401
class to create an executor
Definition executor.hpp:62
class to derive an executor observer
Definition observer.hpp:169
virtual void set_up(size_t num_workers)=0
constructor-like method to call when the executor observer is fully created
virtual void on_entry(WorkerView wv, TaskView task_view)=0
method to call before a worker thread executes a closure
virtual void on_exit(WorkerView wv, TaskView task_view)=0
method to call after a worker thread executed a closure
virtual ~ObserverInterface()=default
virtual destructor
class to create an observer based on the built-in taskflow profiler format
Definition observer.hpp:441
std::string dump() const
dumps the timelines into a JSON string
Definition observer.hpp:804
void clear()
clears the timeline data
Definition observer.hpp:719
void summary(std::ostream &ostream) const
shows the summary report through an output stream
Definition observer.hpp:811
size_t num_workers() const
queries the number of observed workers
Definition observer.hpp:920
std::string summary() const
returns the summary report in a string
Definition observer.hpp:902
size_t num_tasks() const
queries the number of tasks observed
Definition observer.hpp:909
class to access task information from the observer interface
Definition task.hpp:1235
class to create an immutable view of a worker
Definition worker.hpp:114
taskflow namespace
Definition small_vector.hpp:20
T unique_id()
generates a program-wide unique ID of the given type in a thread-safe manner
Definition math.hpp:182
TaskType
enumeration of all task types
Definition task.hpp:21
@ UNDEFINED
undefined task type (for internal use only)
Definition task.hpp:37
const char * to_string(TaskType type)
convert a task type to a human-readable string
Definition task.hpp:66
ObserverType
enumeration of all observer types
Definition observer.hpp:1032
std::chrono::time_point< std::chrono::steady_clock > observer_stamp_t
default time point type of observers
Definition observer.hpp:20
std::string get_env(const std::string &str)
retrieves the value of an environment variable
Definition os.hpp:183