Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:55:52

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2023, Wouter Deconinck
0003 
0004 #include <JANA/JEventProcessor.h>
0005 
0006 #include <map>
0007 #include <string>
0008 
0009 class JEventProcessorJANATOP : public JEventProcessor {
0010 private:
0011   enum node_type { kDefault, kProcessor, kFactory, kCache, kSource };
0012 
0013   class CallLink {
0014   public:
0015     std::string caller_name;
0016     std::string caller_tag;
0017     std::string callee_name;
0018     std::string callee_tag;
0019 
0020     bool operator<(const CallLink& link) const {
0021       if (this->caller_name != link.caller_name)
0022         return this->caller_name < link.caller_name;
0023       if (this->callee_name != link.callee_name)
0024         return this->callee_name < link.callee_name;
0025       if (this->caller_tag != link.caller_tag)
0026         return this->caller_tag < link.caller_tag;
0027       return this->callee_tag < link.callee_tag;
0028     }
0029   };
0030 
0031   class CallStats {
0032   public:
0033     CallStats(void) {
0034       from_cache_ms         = 0;
0035       from_source_ms        = 0;
0036       from_factory_ms       = 0;
0037       data_not_available_ms = 0;
0038       Nfrom_cache           = 0;
0039       Nfrom_source          = 0;
0040       Nfrom_factory         = 0;
0041       Ndata_not_available   = 0;
0042     }
0043     double from_cache_ms;
0044     double from_source_ms;
0045     double from_factory_ms;
0046     double data_not_available_ms;
0047     unsigned int Nfrom_cache;
0048     unsigned int Nfrom_source;
0049     unsigned int Nfrom_factory;
0050     unsigned int Ndata_not_available;
0051   };
0052 
0053   class FactoryCallStats {
0054   public:
0055     FactoryCallStats(void) {
0056       type           = kDefault;
0057       time_waited_on = 0.0;
0058       time_waiting   = 0.0;
0059       Nfrom_factory  = 0;
0060       Nfrom_source   = 0;
0061       Nfrom_cache    = 0;
0062     }
0063     node_type type;
0064     double time_waited_on; // time other factories spent waiting on this factory
0065     double time_waiting;   // time this factory spent waiting on other factories
0066     unsigned int Nfrom_factory;
0067     unsigned int Nfrom_source;
0068     unsigned int Nfrom_cache;
0069   };
0070 
0071 public:
0072   JEventProcessorJANATOP() : JEventProcessor() { SetTypeName("JEventProcessorJANATOP"); };
0073 
0074   void Init() override{};
0075 
0076   void BeginRun(const std::shared_ptr<const JEvent>& /* event */) override{};
0077 
0078   void Process(const std::shared_ptr<const JEvent>& event) override {
0079     // Get the call stack for ths event and add the results to our stats
0080     auto stack = event->GetJCallGraphRecorder()->GetCallGraph();
0081 
0082     // Lock mutex in case we are running with multiple threads
0083     std::lock_guard<std::mutex> lck(mutex);
0084 
0085     // Loop over the call stack elements and add in the values
0086     for (unsigned int i = 0; i < stack.size(); i++) {
0087 
0088       // Keep track of total time each factory spent waiting and being waited on
0089       std::string nametag1 = MakeNametag(stack[i].caller_name, stack[i].caller_tag);
0090       std::string nametag2 = MakeNametag(stack[i].callee_name, stack[i].callee_tag);
0091 
0092       FactoryCallStats& fcallstats1 = factory_stats[nametag1];
0093       FactoryCallStats& fcallstats2 = factory_stats[nametag2];
0094 
0095       auto delta_t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(stack[i].end_time -
0096                                                                               stack[i].start_time)
0097                             .count();
0098       fcallstats1.time_waiting += delta_t_ms;
0099       fcallstats2.time_waited_on += delta_t_ms;
0100 
0101       // Get pointer to CallStats object representing this calling pair
0102       CallLink link;
0103       link.caller_name = stack[i].caller_name;
0104       link.caller_tag  = stack[i].caller_tag;
0105       link.callee_name = stack[i].callee_name;
0106       link.callee_tag  = stack[i].callee_tag;
0107       CallStats& stats =
0108           call_links[link]; // get pointer to stats object or create if it doesn't exist
0109 
0110       switch (stack[i].data_source) {
0111       case JCallGraphRecorder::DATA_NOT_AVAILABLE:
0112         stats.Ndata_not_available++;
0113         stats.data_not_available_ms += delta_t_ms;
0114         break;
0115       case JCallGraphRecorder::DATA_FROM_CACHE:
0116         fcallstats2.Nfrom_cache++;
0117         stats.Nfrom_cache++;
0118         stats.from_cache_ms += delta_t_ms;
0119         break;
0120       case JCallGraphRecorder::DATA_FROM_SOURCE:
0121         fcallstats2.Nfrom_source++;
0122         stats.Nfrom_source++;
0123         stats.from_source_ms += delta_t_ms;
0124         break;
0125       case JCallGraphRecorder::DATA_FROM_FACTORY:
0126         fcallstats2.Nfrom_factory++;
0127         stats.Nfrom_factory++;
0128         stats.from_factory_ms += delta_t_ms;
0129         break;
0130       }
0131     }
0132   };
0133 
0134   void EndRun() override{};
0135 
0136   void Finish() override {
0137     // In order to get the total time we have to first get a list of
0138     // the event processors (i.e. top-level callers). We can tell
0139     // this just by looking for callers that never show up as callees
0140     std::set<std::string> callers;
0141     std::set<std::string> callees;
0142     for (auto iter = call_links.begin(); iter != call_links.end(); iter++) {
0143       const CallLink& link = iter->first;
0144       std::string caller   = MakeNametag(link.caller_name, link.caller_tag);
0145       std::string callee   = MakeNametag(link.callee_name, link.callee_tag);
0146       callers.insert(caller);
0147       callees.insert(callee);
0148     }
0149 
0150     // Loop over list a second time so we can get the total ticks for
0151     // the process in order to add the percentage to the label below
0152     double total_ms = 0.0;
0153     for (auto iter = call_links.begin(); iter != call_links.end(); iter++) {
0154       const CallLink& link   = iter->first;
0155       const CallStats& stats = iter->second;
0156       std::string caller     = MakeNametag(link.caller_name, link.caller_tag);
0157       std::string callee     = MakeNametag(link.callee_name, link.callee_tag);
0158       if (callees.find(caller) == callees.end()) {
0159         total_ms += stats.from_factory_ms + stats.from_source_ms + stats.from_cache_ms +
0160                     stats.data_not_available_ms;
0161       }
0162     }
0163     if (total_ms == 0.0)
0164       total_ms = 1.0;
0165 
0166     // Loop over call links
0167     std::cout << "Links:" << std::endl;
0168     std::vector<std::pair<CallLink, CallStats>> call_links_vector{
0169         std::make_move_iterator(std::begin(call_links)),
0170         std::make_move_iterator(std::end(call_links))};
0171     [[maybe_unused]] auto call_links_compare_time = [](const auto& a, const auto& b) {
0172       return ((a.second.from_factory_ms + a.second.from_source_ms + a.second.from_cache_ms) <
0173               (b.second.from_factory_ms + b.second.from_source_ms + b.second.from_cache_ms));
0174     };
0175     [[maybe_unused]] auto call_links_compare_N = [](const auto& a, const auto& b) {
0176       return ((a.second.Nfrom_factory + a.second.Nfrom_source + a.second.Nfrom_cache) <
0177               (b.second.Nfrom_factory + b.second.Nfrom_source + b.second.Nfrom_cache));
0178     };
0179     std::sort(call_links_vector.begin(), call_links_vector.end(), call_links_compare_time);
0180     for (auto iter = call_links_vector.end() - std::min(call_links_vector.size(), 10ul);
0181          iter != call_links_vector.end(); iter++) {
0182       const CallLink& link   = iter->first;
0183       const CallStats& stats = iter->second;
0184 
0185       unsigned int Ntotal = stats.Nfrom_cache + stats.Nfrom_source + stats.Nfrom_factory;
0186 
0187       std::string nametag1 = MakeNametag(link.caller_name, link.caller_tag);
0188       std::string nametag2 = MakeNametag(link.callee_name, link.callee_tag);
0189 
0190       double this_ms      = stats.from_factory_ms + stats.from_source_ms + stats.from_cache_ms;
0191       std::string timestr = MakeTimeString(stats.from_factory_ms);
0192       double percent      = 100.0 * this_ms / total_ms;
0193       char percentstr[32];
0194       snprintf(percentstr, 32, "%5.1f%%", percent);
0195 
0196       std::cout << Ntotal << " calls, " << timestr << " (" << percentstr << ") ";
0197       std::cout << nametag1 << " -> " << nametag2;
0198       std::cout << std::endl;
0199     }
0200 
0201     // Loop over factories
0202     std::cout << "Factories:" << std::endl;
0203     std::vector<std::pair<std::string, FactoryCallStats>> factory_stats_vector{
0204         std::make_move_iterator(std::begin(factory_stats)),
0205         std::make_move_iterator(std::end(factory_stats))};
0206     auto factory_stats_compare = [](const auto& a, const auto& b) {
0207       return ((a.second.time_waited_on - a.second.time_waiting) <
0208               (b.second.time_waited_on - b.second.time_waiting));
0209     };
0210     std::sort(factory_stats_vector.begin(), factory_stats_vector.end(), factory_stats_compare);
0211     for (auto iter = factory_stats_vector.end() - std::min(factory_stats_vector.size(), 10ul);
0212          iter != factory_stats_vector.end(); iter++) {
0213       FactoryCallStats& fcall_stats = iter->second;
0214       std::string nodename          = iter->first;
0215 
0216       // Get time spent in this factory proper
0217       double time_spent_in_factory = fcall_stats.time_waited_on - fcall_stats.time_waiting;
0218       std::string timestr          = MakeTimeString(time_spent_in_factory);
0219       double percent               = 100.0 * time_spent_in_factory / total_ms;
0220       char percentstr[32];
0221       snprintf(percentstr, 32, "%5.1f%%", percent);
0222 
0223       std::cout << timestr << " (" << percentstr << ") ";
0224       std::cout << nodename;
0225       std::cout << std::endl;
0226     }
0227   };
0228 
0229 private:
0230   std::mutex mutex;
0231 
0232   std::map<CallLink, CallStats> call_links;
0233   std::map<std::string, FactoryCallStats> factory_stats;
0234 
0235   std::string MakeTimeString(double time_in_ms) {
0236     double order = log10(time_in_ms);
0237     std::stringstream ss;
0238     ss << std::fixed << std::setprecision(2);
0239     if (order < 0) {
0240       ss << time_in_ms * 1000.0 << " us";
0241     } else if (order <= 2.0) {
0242       ss << time_in_ms << " ms";
0243     } else {
0244       ss << time_in_ms / 1000.0 << " s";
0245     }
0246     return ss.str();
0247   }
0248 
0249   std::string MakeNametag(const std::string& name, const std::string& tag) {
0250     std::string nametag = name;
0251     if (tag.size() > 0)
0252       nametag += ":" + tag;
0253     return nametag;
0254   }
0255 };