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