1 #ifndef NETGEN_CORE_PROFILER_HPP
2 #define NETGEN_CORE_PROFILER_HPP
3 
4 #include <array>
5 #include <chrono>
6 #include <functional>
7 #include <string>
8 
9 #include "array.hpp"
10 #include "logging.hpp"
11 #include "paje_trace.hpp"
12 #include "taskmanager.hpp"
13 #include "utils.hpp"
14 
15 namespace ngcore
16 {
17   class NgProfiler
18   {
19   public:
20     /// maximal number of timers
21     enum { SIZE = 8*1024 };
22 
23     struct TimerVal
24     {
25         TimerVal() = default;
26 
27         double tottime = 0.0;
28         TTimePoint starttime=0;
29         double flops = 0.0;
30         double loads = 0.0;
31         double stores = 0.0;
32         long count = 0;
33         std::string name = "";
34         int usedcounter = 0;
35     };
36 
37     NGCORE_API static std::vector<TimerVal> timers;
38 
39     NGCORE_API static TTimePoint * thread_times;
40     NGCORE_API static TTimePoint * thread_flops;
41     NGCORE_API static std::shared_ptr<Logger> logger;
42     NGCORE_API static std::array<size_t, NgProfiler::SIZE> dummy_thread_times;
43     NGCORE_API static std::array<size_t, NgProfiler::SIZE> dummy_thread_flops;
44   private:
45 
46     NGCORE_API static std::string filename;
47   public:
48     NgProfiler();
49     ~NgProfiler();
50 
51     NgProfiler(const NgProfiler &) = delete;
52     NgProfiler(NgProfiler &&) = delete;
53     void operator=(const NgProfiler &) = delete;
54     void operator=(NgProfiler &&) = delete;
55 
SetFileName(const std::string & afilename)56     static void SetFileName (const std::string & afilename) { filename = afilename; }
57 
58     /// create new timer, use integer index
59     NGCORE_API static int CreateTimer (const std::string & name);
60 
61     NGCORE_API static void Reset ();
62 
63 
64     /// start timer of index nr
StartTimer(int nr)65     static void StartTimer (int nr)
66     {
67       timers[nr].starttime = GetTimeCounter(); timers[nr].count++;
68     }
69 
70     /// stop timer of index nr
StopTimer(int nr)71     static void StopTimer (int nr)
72     {
73       timers[nr].tottime += (GetTimeCounter()-timers[nr].starttime)*seconds_per_tick;
74     }
75 
StartThreadTimer(size_t nr,size_t tid)76     static void StartThreadTimer (size_t nr, size_t tid)
77     {
78       thread_times[tid*SIZE+nr] -= GetTimeCounter(); // NOLINT
79     }
80 
StopThreadTimer(size_t nr,size_t tid)81     static void StopThreadTimer (size_t nr, size_t tid)
82     {
83       thread_times[tid*SIZE+nr] += GetTimeCounter(); // NOLINT
84     }
85 
AddThreadFlops(size_t nr,size_t tid,size_t flops)86     static void AddThreadFlops (size_t nr, size_t tid, size_t flops)
87     {
88       thread_flops[tid*SIZE+nr] += flops; // NOLINT
89     }
90 
91     /// if you know number of flops, provide them to obtain the MFlop - rate
AddFlops(int nr,double aflops)92     static void AddFlops (int nr, double aflops) { timers[nr].flops += aflops; }
AddLoads(int nr,double aloads)93     static void AddLoads (int nr, double aloads) { timers[nr].loads += aloads; }
AddStores(int nr,double astores)94     static void AddStores (int nr, double astores) { timers[nr].stores += astores; }
95 
GetNr(const std::string & name)96     static int GetNr (const std::string & name)
97     {
98       for (int i = SIZE-1; i >= 0; i--)
99         if (timers[i].name == name)
100           return i;
101       return -1;
102     }
103 
GetTime(int nr)104     static double GetTime (int nr)
105     {
106       return timers[nr].tottime;
107     }
108 
GetTime(const std::string & name)109     static double GetTime (const std::string & name)
110     {
111       for (int i = SIZE-1; i >= 0; i--)
112         if (timers[i].name == name)
113           return GetTime (i);
114       return 0;
115     }
116 
GetCounts(int nr)117     static long int GetCounts (int nr)
118     {
119       return timers[nr].count;
120     }
121 
GetFlops(int nr)122     static double GetFlops (int nr)
123     {
124       return timers[nr].flops;
125     }
126 
127     /// change name
SetName(int nr,const std::string & name)128     static void SetName (int nr, const std::string & name) { timers[nr].name = name; }
GetName(int nr)129     static std::string GetName (int nr) { return timers[nr].name; }
130     /// print profile
131     NGCORE_API static void Print (FILE * prof);
132 
133     class RegionTimer
134     {
135       int nr;
136     public:
137       /// start timer
RegionTimer(int anr)138       RegionTimer (int anr) : nr(anr) { NgProfiler::StartTimer(nr); }
139       /// stop timer
~RegionTimer()140       ~RegionTimer () { NgProfiler::StopTimer(nr); }
141 
142       RegionTimer() = delete;
143       RegionTimer(const RegionTimer &) = delete;
144       RegionTimer(RegionTimer &&) = delete;
145       void operator=(const RegionTimer &) = delete;
146       void operator=(RegionTimer &&) = delete;
147     };
148   };
149 
150 
151   struct TNoTracing{ static constexpr bool do_tracing=false; };
152   struct TTracing{ static constexpr bool do_tracing=true; };
153 
154   struct TNoTiming{ static constexpr bool do_timing=false; };
155   struct TTiming{ static constexpr bool do_timing=true; };
156 
157   namespace detail {
158 
159       template<typename T>
160       constexpr bool is_tracing_type_v = std::is_same_v<T, TNoTracing> || std::is_same_v<T, TTracing>;
161 
162       template<typename T>
163       constexpr bool is_timing_type_v = std::is_same_v<T, TNoTiming> || std::is_same_v<T, TTiming>;
164   }
165 
166   static TNoTracing NoTracing;
167   static TNoTiming NoTiming;
168 
169   template<typename TTracing=TTracing, typename TTiming=TTiming>
170   class Timer
171   {
172     int timernr;
Init(const std::string & name)173     int Init( const std::string & name )
174     {
175       return NgProfiler::CreateTimer (name);
176     }
177   public:
178     static constexpr bool do_tracing = TTracing::do_tracing;
179     static constexpr bool do_timing = TTiming::do_timing;
180 
Timer(const std::string & name)181     Timer (const std::string & name) : timernr(Init(name)) { }
182 
183     template<std::enable_if_t< detail::is_tracing_type_v<TTracing>, bool> = false>
Timer(const std::string & name,TTracing)184     Timer( const std::string & name, TTracing ) : timernr(Init(name)) { }
185 
186     template<std::enable_if_t< detail::is_timing_type_v<TTiming>, bool> = false>
Timer(const std::string & name,TTiming)187     Timer( const std::string & name, TTiming ) : timernr(Init(name)) { }
188 
Timer(const std::string & name,TTracing,TTiming)189     Timer( const std::string & name, TTracing, TTiming ) : timernr(Init(name)) { }
190 
Timer(const std::string & name,int)191     [[deprecated ("Use Timer(name, NoTracing/NoTiming) instead")]] Timer( const std::string & name, int ) : timernr(Init(name)) {}
192 
SetName(const std::string & name)193     void SetName (const std::string & name)
194     {
195       NgProfiler::SetName (timernr, name);
196     }
Start() const197     void Start () const
198     {
199       Start(TaskManager::GetThreadId());
200     }
Stop() const201     void Stop () const
202     {
203       Stop(TaskManager::GetThreadId());
204     }
Start(int tid) const205     void Start (int tid) const
206     {
207         if(tid==0)
208         {
209           if constexpr(do_timing)
210             NgProfiler::StartTimer (timernr);
211           if constexpr(do_tracing)
212             if(trace) trace->StartTimer(timernr);
213         }
214         else
215         {
216           if constexpr(do_timing)
217             NgProfiler::StartThreadTimer(timernr, tid);
218           if constexpr(do_tracing)
219             if(trace) trace->StartTask (tid, timernr, PajeTrace::Task::ID_TIMER);
220         }
221     }
Stop(int tid) const222     void Stop (int tid) const
223     {
224         if(tid==0)
225         {
226             if constexpr(do_timing)
227                 NgProfiler::StopTimer (timernr);
228             if constexpr(do_tracing)
229                 if(trace) trace->StopTimer(timernr);
230         }
231         else
232         {
233           if constexpr(do_timing)
234             NgProfiler::StopThreadTimer(timernr, tid);
235           if constexpr(do_tracing)
236             if(trace) trace->StopTask (tid, timernr, PajeTrace::Task::ID_TIMER);
237         }
238     }
AddFlops(double aflops)239     void AddFlops (double aflops)
240     {
241       if constexpr(do_timing)
242 	NgProfiler::AddFlops (timernr, aflops);
243     }
244 
GetTime()245     double GetTime () { return NgProfiler::GetTime(timernr); }
GetCounts()246     long int GetCounts () { return NgProfiler::GetCounts(timernr); }
GetMFlops()247     double GetMFlops ()
248     { return NgProfiler::GetFlops(timernr)
249         / NgProfiler::GetTime(timernr) * 1e-6; }
operator int()250     operator int () { return timernr; }
251   };
252 
253 
254   /**
255      Timer object.
256        Start / stop timer at constructor / destructor.
257   */
258   template<typename TTimer>
259   class RegionTimer
260   {
261     const TTimer & timer;
262     int tid;
263   public:
264     /// start timer
RegionTimer(const TTimer & atimer)265     RegionTimer (const TTimer & atimer) : timer(atimer)
266     {
267       tid = TaskManager::GetThreadId();
268       timer.Start(tid);
269     }
270 
271     /// stop timer
~RegionTimer()272     ~RegionTimer () { timer.Stop(tid); }
273 
274     RegionTimer() = delete;
275     RegionTimer(const RegionTimer &) = delete;
276     RegionTimer(RegionTimer &&) = delete;
277     void operator=(const RegionTimer &) = delete;
278     void operator=(RegionTimer &&) = delete;
279   };
280 
281   class [[deprecated("Use RegionTimer instead (now thread safe)")]] ThreadRegionTimer
282   {
283     size_t nr;
284     size_t tid;
285   public:
286     /// start timer
ThreadRegionTimer(size_t _nr,size_t _tid)287     ThreadRegionTimer (size_t _nr, size_t _tid) : nr(_nr), tid(_tid)
288     { NgProfiler::StartThreadTimer(nr, tid); }
289     /// stop timer
~ThreadRegionTimer()290     ~ThreadRegionTimer ()
291     { NgProfiler::StopThreadTimer(nr, tid); }
292 
293     ThreadRegionTimer() = delete;
294     ThreadRegionTimer(ThreadRegionTimer &&) = delete;
295     ThreadRegionTimer(const ThreadRegionTimer &) = delete;
296     void operator=(const ThreadRegionTimer &) = delete;
297     void operator=(ThreadRegionTimer &&) = delete;
298   };
299 
300   class RegionTracer
301     {
302       int nr;
303       int thread_id;
304       int type;
305     public:
306       static constexpr int ID_JOB = PajeTrace::Task::ID_JOB;
307       static constexpr int ID_NONE = PajeTrace::Task::ID_NONE;
308       static constexpr int ID_TIMER = PajeTrace::Task::ID_TIMER;
309 
310       RegionTracer() = delete;
311       RegionTracer(RegionTracer &&) = delete;
312       RegionTracer(const RegionTracer &) = delete;
313       void operator=(const RegionTracer &) = delete;
314       void operator=(RegionTracer &&) = delete;
315 
316       /// start trace
RegionTracer(int athread_id,int region_id,int id_type=ID_NONE,int additional_value=-1)317       RegionTracer (int athread_id, int region_id, int id_type = ID_NONE, int additional_value = -1 )
318         : thread_id(athread_id)
319         {
320 	  if (trace)
321           trace->StartTask (athread_id, region_id, id_type, additional_value);
322           type = id_type;
323           nr = region_id;
324         }
325       /// start trace with timer
326       template<typename TTimer>
RegionTracer(int athread_id,TTimer & timer,int additional_value=-1)327       RegionTracer (int athread_id, TTimer & timer, int additional_value = -1 )
328         : thread_id(athread_id)
329         {
330           nr = timer;
331           type = ID_TIMER;
332 	  if (trace)
333             trace->StartTask (athread_id, nr, type, additional_value);
334         }
335 
336       /// stop trace
~RegionTracer()337       ~RegionTracer ()
338         {
339 	  if (trace)
340             trace->StopTask (thread_id, nr, type);
341         }
342     };
343 
344 
345   // Helper function for timings
346   // Run f() at least min_iterations times until max_time seconds elapsed
347   // returns minimum runtime for a call of f()
348   template<typename TFunc>
RunTiming(TFunc f,double max_time=0.5,int min_iterations=10)349   double RunTiming( TFunc f, double max_time = 0.5, int min_iterations = 10 )
350   {
351       // Make sure the whole test run does not exceed maxtime
352       double tend = WallTime()+max_time;
353 
354       // warmup
355       f();
356 
357       double tres = std::numeric_limits<double>::max();
358       int iteration = 0;
359       while(WallTime()<tend || iteration++ < min_iterations)
360       {
361           double t = -WallTime();
362           f();
363           t += WallTime();
364           tres = std::min(tres, t);
365       }
366 
367       return tres;
368   }
369 
370 } // namespace ngcore
371 
372 // Helper macro to easily add multiple timers in a function for profiling
373 // Usage: NETGEN_TIMER_FROM_HERE("my_timer_name")
374 // Effect: define static Timer and RegionTimer with given name and line number
375 #define NETGEN_TOKEN_CONCAT(x, y) x ## y
376 #define NETGEN_TOKEN_CONCAT2(x, y) NETGEN_TOKEN_CONCAT(x, y)
377 #define NETGEN_TIMER_FROM_HERE(name) \
378   static Timer NETGEN_TOKEN_CONCAT2(timer_, __LINE__)( string(name)+"_"+ToString(__LINE__)); \
379   RegionTimer NETGEN_TOKEN_CONCAT2(rt_,__LINE__)(NETGEN_TOKEN_CONCAT2(timer_,__LINE__));
380 
381 
382 #endif // NETGEN_CORE_PROFILER_HPP
383