EICrecon
JANA based reconstruction for the EPIC detector
Loading...
Searching...
No Matches
JEventProcessorJANATOP.h
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (C) 2023, Wouter Deconinck
3
4#pragma once
5
6#include <JANA/JEventProcessor.h>
7
8#include <map>
9#include <string>
10
11class JEventProcessorJANATOP : public JEventProcessor {
12private:
13 enum node_type { kDefault, kProcessor, kFactory, kCache, kSource };
14
15 class CallLink {
16 public:
17 std::string caller_name;
18 std::string caller_tag;
19 std::string callee_name;
20 std::string callee_tag;
21
22 bool operator<(const CallLink& link) const {
23 if (this->caller_name != link.caller_name)
24 return this->caller_name < link.caller_name;
25 if (this->callee_name != link.callee_name)
26 return this->callee_name < link.callee_name;
27 if (this->caller_tag != link.caller_tag)
28 return this->caller_tag < link.caller_tag;
29 return this->callee_tag < link.callee_tag;
30 }
31 };
32
33 class CallStats {
34 public:
35 CallStats(void) {
36 from_cache_ms = 0;
37 from_source_ms = 0;
38 from_factory_ms = 0;
39 data_not_available_ms = 0;
40 Nfrom_cache = 0;
41 Nfrom_source = 0;
42 Nfrom_factory = 0;
43 Ndata_not_available = 0;
44 }
45 double from_cache_ms;
46 double from_source_ms;
47 double from_factory_ms;
48 double data_not_available_ms;
49 unsigned int Nfrom_cache;
50 unsigned int Nfrom_source;
51 unsigned int Nfrom_factory;
52 unsigned int Ndata_not_available;
53 };
54
55 class FactoryCallStats {
56 public:
57 FactoryCallStats(void) {
58 type = kDefault;
59 time_waited_on = 0.0;
60 time_waiting = 0.0;
61 Nfrom_factory = 0;
62 Nfrom_source = 0;
63 Nfrom_cache = 0;
64 }
65 node_type type;
66 double time_waited_on; // time other factories spent waiting on this factory
67 double time_waiting; // time this factory spent waiting on other factories
68 unsigned int Nfrom_factory;
69 unsigned int Nfrom_source;
70 unsigned int Nfrom_cache;
71 };
72
73public:
74 JEventProcessorJANATOP() : JEventProcessor() { SetTypeName("JEventProcessorJANATOP"); };
75
76 void Init() override {};
77
78 void BeginRun(const std::shared_ptr<const JEvent>& /* event */) override {};
79
80 void Process(const std::shared_ptr<const JEvent>& event) override {
81 // Get the call stack for ths event and add the results to our stats
82 auto stack = event->GetJCallGraphRecorder()->GetCallGraph();
83
84 // Lock mutex in case we are running with multiple threads
85 std::lock_guard<std::mutex> lck(mutex);
86
87 // Loop over the call stack elements and add in the values
88 for (unsigned int i = 0; i < stack.size(); i++) {
89
90 // Keep track of total time each factory spent waiting and being waited on
91 std::string nametag1 = MakeNametag(stack[i].caller_name, stack[i].caller_tag);
92 std::string nametag2 = MakeNametag(stack[i].callee_name, stack[i].callee_tag);
93
94 FactoryCallStats& fcallstats1 = factory_stats[nametag1];
95 FactoryCallStats& fcallstats2 = factory_stats[nametag2];
96
97 auto delta_t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(stack[i].end_time -
98 stack[i].start_time)
99 .count();
100 fcallstats1.time_waiting += delta_t_ms;
101 fcallstats2.time_waited_on += delta_t_ms;
102
103 // Get pointer to CallStats object representing this calling pair
104 CallLink link;
105 link.caller_name = stack[i].caller_name;
106 link.caller_tag = stack[i].caller_tag;
107 link.callee_name = stack[i].callee_name;
108 link.callee_tag = stack[i].callee_tag;
109 CallStats& stats =
110 call_links[link]; // get pointer to stats object or create if it doesn't exist
111
112 switch (stack[i].data_source) {
113 case JCallGraphRecorder::DATA_NOT_AVAILABLE:
114 stats.Ndata_not_available++;
115 stats.data_not_available_ms += delta_t_ms;
116 break;
117 case JCallGraphRecorder::DATA_FROM_CACHE:
118 fcallstats2.Nfrom_cache++;
119 stats.Nfrom_cache++;
120 stats.from_cache_ms += delta_t_ms;
121 break;
122 case JCallGraphRecorder::DATA_FROM_SOURCE:
123 fcallstats2.Nfrom_source++;
124 stats.Nfrom_source++;
125 stats.from_source_ms += delta_t_ms;
126 break;
127 case JCallGraphRecorder::DATA_FROM_FACTORY:
128 fcallstats2.Nfrom_factory++;
129 stats.Nfrom_factory++;
130 stats.from_factory_ms += delta_t_ms;
131 break;
132 }
133 }
134 };
135
136 void EndRun() override {};
137
138 void Finish() override {
139 // In order to get the total time we have to first get a list of
140 // the event processors (i.e. top-level callers). We can tell
141 // this just by looking for callers that never show up as callees
142 std::set<std::string> callers;
143 std::set<std::string> callees;
144 for (auto iter = call_links.begin(); iter != call_links.end(); iter++) {
145 const CallLink& link = iter->first;
146 std::string caller = MakeNametag(link.caller_name, link.caller_tag);
147 std::string callee = MakeNametag(link.callee_name, link.callee_tag);
148 callers.insert(caller);
149 callees.insert(callee);
150 }
151
152 // Loop over list a second time so we can get the total ticks for
153 // the process in order to add the percentage to the label below
154 double total_ms = 0.0;
155 for (auto iter = call_links.begin(); iter != call_links.end(); iter++) {
156 const CallLink& link = iter->first;
157 const CallStats& stats = iter->second;
158 std::string caller = MakeNametag(link.caller_name, link.caller_tag);
159 std::string callee = MakeNametag(link.callee_name, link.callee_tag);
160 if (callees.find(caller) == callees.end()) {
161 total_ms += stats.from_factory_ms + stats.from_source_ms + stats.from_cache_ms +
162 stats.data_not_available_ms;
163 }
164 }
165 if (total_ms == 0.0)
166 total_ms = 1.0;
167
168 // Loop over call links
169 std::cout << "Links:" << std::endl;
170 std::vector<std::pair<CallLink, CallStats>> call_links_vector{
171 std::make_move_iterator(std::begin(call_links)),
172 std::make_move_iterator(std::end(call_links))};
173 [[maybe_unused]] auto call_links_compare_time = [](const auto& a, const auto& b) {
174 return ((a.second.from_factory_ms + a.second.from_source_ms + a.second.from_cache_ms) <
175 (b.second.from_factory_ms + b.second.from_source_ms + b.second.from_cache_ms));
176 };
177 [[maybe_unused]] auto call_links_compare_N = [](const auto& a, const auto& b) {
178 return ((a.second.Nfrom_factory + a.second.Nfrom_source + a.second.Nfrom_cache) <
179 (b.second.Nfrom_factory + b.second.Nfrom_source + b.second.Nfrom_cache));
180 };
181 std::sort(call_links_vector.begin(), call_links_vector.end(), call_links_compare_time);
182 for (auto iter = call_links_vector.end() - std::min(call_links_vector.size(), 10ul);
183 iter != call_links_vector.end(); iter++) {
184 const CallLink& link = iter->first;
185 const CallStats& stats = iter->second;
186
187 unsigned int Ntotal = stats.Nfrom_cache + stats.Nfrom_source + stats.Nfrom_factory;
188
189 std::string nametag1 = MakeNametag(link.caller_name, link.caller_tag);
190 std::string nametag2 = MakeNametag(link.callee_name, link.callee_tag);
191
192 double this_ms = stats.from_factory_ms + stats.from_source_ms + stats.from_cache_ms;
193 std::string timestr = MakeTimeString(stats.from_factory_ms);
194 double percent = 100.0 * this_ms / total_ms;
195 char percentstr[32];
196 snprintf(percentstr, 32, "%5.1f%%", percent);
197
198 std::cout << Ntotal << " calls, " << timestr << " (" << percentstr << ") ";
199 std::cout << nametag1 << " -> " << nametag2;
200 std::cout << std::endl;
201 }
202
203 // Loop over factories
204 std::cout << "Factories:" << std::endl;
205 std::vector<std::pair<std::string, FactoryCallStats>> factory_stats_vector{
206 std::make_move_iterator(std::begin(factory_stats)),
207 std::make_move_iterator(std::end(factory_stats))};
208 auto factory_stats_compare = [](const auto& a, const auto& b) {
209 return ((a.second.time_waited_on - a.second.time_waiting) <
210 (b.second.time_waited_on - b.second.time_waiting));
211 };
212 std::sort(factory_stats_vector.begin(), factory_stats_vector.end(), factory_stats_compare);
213 for (auto iter = factory_stats_vector.end() - std::min(factory_stats_vector.size(), 10ul);
214 iter != factory_stats_vector.end(); iter++) {
215 FactoryCallStats& fcall_stats = iter->second;
216 std::string nodename = iter->first;
217
218 // Get time spent in this factory proper
219 double time_spent_in_factory = fcall_stats.time_waited_on - fcall_stats.time_waiting;
220 std::string timestr = MakeTimeString(time_spent_in_factory);
221 double percent = 100.0 * time_spent_in_factory / total_ms;
222 char percentstr[32];
223 snprintf(percentstr, 32, "%5.1f%%", percent);
224
225 std::cout << timestr << " (" << percentstr << ") ";
226 std::cout << nodename;
227 std::cout << std::endl;
228 }
229 };
230
231private:
232 std::mutex mutex;
233
234 std::map<CallLink, CallStats> call_links;
235 std::map<std::string, FactoryCallStats> factory_stats;
236
237 std::string MakeTimeString(double time_in_ms) {
238 double order = log10(time_in_ms);
239 std::stringstream ss;
240 ss << std::fixed << std::setprecision(2);
241 if (order < 0) {
242 ss << time_in_ms * 1000.0 << " us";
243 } else if (order <= 2.0) {
244 ss << time_in_ms << " ms";
245 } else {
246 ss << time_in_ms / 1000.0 << " s";
247 }
248 return ss.str();
249 }
250
251 std::string MakeNametag(const std::string& name, const std::string& tag) {
252 std::string nametag = name;
253 if (tag.size() > 0)
254 nametag += ":" + tag;
255 return nametag;
256 }
257};
Definition JEventProcessorJANATOP.h:11
JEventProcessorJANATOP()
Definition JEventProcessorJANATOP.h:74
void Init() override
Definition JEventProcessorJANATOP.h:76
void Finish() override
Definition JEventProcessorJANATOP.h:138
void Process(const std::shared_ptr< const JEvent > &event) override
Definition JEventProcessorJANATOP.h:80
void BeginRun(const std::shared_ptr< const JEvent > &) override
Definition JEventProcessorJANATOP.h:78
void EndRun() override
Definition JEventProcessorJANATOP.h:136