Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:17:54

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