LCOV - code coverage report
Current view: top level - source/lib - timer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 38 63 60.3 %
Date: 2023-01-19 00:18:29 Functions: 8 11 72.7 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : /*
      24             :  * platform-independent high resolution timer
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : #include "lib/timer.h"
      29             : 
      30             : #include <cfloat>
      31             : #include <cmath>
      32             : #include <cstdarg>
      33             : #include <mutex>
      34             : #include <numeric>
      35             : #include <sstream>    // std::stringstream
      36             : 
      37             : #include "lib/module_init.h"
      38             : #include "lib/posix/posix_time.h"
      39             : #include "lib/sysdep/cpu.h"
      40             : 
      41             : #if OS_WIN
      42             : #include "lib/sysdep/os/win/win.h"
      43             : #endif
      44             : #if OS_UNIX
      45             : # include <unistd.h>
      46             : #endif
      47             : 
      48             : #if OS_UNIX || OS_WIN
      49             : # define HAVE_GETTIMEOFDAY 1
      50             : #else
      51             : # define HAVE_GETTIMEOFDAY 0
      52             : #endif
      53             : 
      54             : #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0)
      55             : # define HAVE_CLOCK_GETTIME 1
      56             : #else
      57             : # define HAVE_CLOCK_GETTIME 0
      58             : #endif
      59             : 
      60             : // rationale for wrapping gettimeofday and clock_gettime, instead of just
      61             : // emulating them where not available: allows returning higher-resolution
      62             : // timer values than their us / ns interface, via double [seconds].
      63             : // they're also not guaranteed to be monotonic.
      64             : 
      65             : #if OS_WIN
      66             : static LARGE_INTEGER start;
      67             : #elif HAVE_CLOCK_GETTIME
      68             : static struct timespec start;
      69             : #elif HAVE_GETTIMEOFDAY
      70             : static struct timeval start;
      71             : #endif
      72             : 
      73             : 
      74             : //-----------------------------------------------------------------------------
      75             : // timer API
      76             : 
      77             : 
      78             : // Cached because the default implementation may take several milliseconds.
      79             : static double resolution;
      80           1 : static Status InitResolution()
      81             : {
      82             : #if OS_WIN
      83             :     LARGE_INTEGER frequency;
      84             :     ENSURE(QueryPerformanceFrequency(&frequency));
      85             :     resolution = 1.0 / static_cast<double>(frequency.QuadPart);
      86             : #elif HAVE_CLOCK_GETTIME
      87             :     struct timespec ts;
      88           1 :     if (clock_getres(CLOCK_MONOTONIC, &ts) == 0)
      89           1 :         resolution = ts.tv_nsec * 1e-9;
      90             :     else
      91           0 :         resolution = 1e-9;
      92             : #elif HAVE_GETTIMEOFDAY
      93             :     resolution = 1e-6;
      94             : #else
      95             :     const double t0 = timer_Time();
      96             :     double t1, t2;
      97             :     do t1 = timer_Time(); while (t1 == t0);
      98             :     do t2 = timer_Time(); while (t2 == t1);
      99             :     resolution = t2 - t1;
     100             : #endif
     101           1 :     return INFO::OK;
     102             : }
     103             : 
     104           1 : void timer_Init()
     105             : {
     106             : #if OS_WIN
     107             :     ENSURE(QueryPerformanceCounter(&start));
     108             : #elif HAVE_CLOCK_GETTIME
     109           1 :     ENSURE(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
     110             : #elif HAVE_GETTIMEOFDAY
     111             :     ENSURE(gettimeofday(&start, 0) == 0);
     112             : #endif
     113             : 
     114             :     static ModuleInitState initState;
     115           1 :     ModuleInit(&initState, InitResolution);
     116           1 :     ENSURE(resolution != 0.0);
     117           1 : }
     118             : 
     119             : static std::mutex ensure_monotonic_mutex;
     120             : // NB: does not guarantee strict monotonicity - callers must avoid
     121             : // dividing by the difference of two equal times.
     122        2014 : static void EnsureMonotonic(double& newTime)
     123             : {
     124        4028 :     std::lock_guard<std::mutex> lock(ensure_monotonic_mutex);
     125             :     static double maxTime;
     126        2014 :     maxTime = std::max(maxTime, newTime);
     127        2014 :     newTime = maxTime;
     128        2014 : }
     129             : 
     130        2014 : double timer_Time()
     131             : {
     132             :     double t;
     133             : 
     134             : #if OS_WIN
     135             :     ENSURE(start.QuadPart); // must have called timer_LatchStartTime first
     136             :     LARGE_INTEGER now;
     137             :     ENSURE(QueryPerformanceCounter(&now));
     138             :     t = static_cast<double>(now.QuadPart - start.QuadPart) * resolution;
     139             : #elif HAVE_CLOCK_GETTIME
     140        2014 :     ENSURE(start.tv_sec || start.tv_nsec);  // must have called timer_LatchStartTime first
     141             :     struct timespec cur;
     142        2014 :     ENSURE(clock_gettime(CLOCK_MONOTONIC, &cur) == 0);
     143        2014 :     t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec) * resolution;
     144             : #elif HAVE_GETTIMEOFDAY
     145             :     ENSURE(start.tv_sec || start.tv_usec);  // must have called timer_LatchStartTime first
     146             :     struct timeval cur;
     147             :     ENSURE(gettimeofday(&cur, 0) == 0);
     148             :     t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec) * resolution;
     149             : #else
     150             : # error "timer_Time: add timer implementation for this platform!"
     151             : #endif
     152             : 
     153        2014 :     EnsureMonotonic(t);
     154        2014 :     return t;
     155             : }
     156             : 
     157             : 
     158           0 : double timer_Resolution()
     159             : {
     160           0 :     return resolution;
     161             : }
     162             : 
     163             : 
     164             : //-----------------------------------------------------------------------------
     165             : // client API
     166             : 
     167             : // intrusive linked-list of all clients. a fixed-size limit would be
     168             : // acceptable (since timers are added manually), but the list is easy
     169             : // to implement and only has the drawback of exposing TimerClient to users.
     170             : //
     171             : // do not use std::list et al. for this! we must be callable at any time,
     172             : // especially before NLSO ctors run or before heap init.
     173             : static size_t numClients;
     174             : static TimerClient* clients;
     175             : 
     176             : 
     177           7 : TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description)
     178             : {
     179           7 :     tc->sum.SetToZero();
     180             : 
     181           7 :     tc->description = description;
     182             : 
     183             :     // insert at front of list
     184           7 :     tc->next = clients;
     185           7 :     clients = tc;
     186           7 :     numClients++;
     187             : 
     188           7 :     return tc;
     189             : }
     190             : 
     191             : 
     192           0 : void timer_DisplayClientTotals()
     193             : {
     194           0 :     debug_printf("TIMER TOTALS (%lu clients)\n", (unsigned long)numClients);
     195           0 :     debug_printf("-----------------------------------------------------\n");
     196             : 
     197           0 :     for(TimerClient* tc = clients; tc; tc = tc->next)
     198             :     {
     199           0 :         const std::string duration = tc->sum.ToString();
     200           0 :         debug_printf("  %s: %s (%lux)\n", utf8_from_wstring(tc->description).c_str(), duration.c_str(), (unsigned long)tc->num_calls);
     201             :     }
     202             : 
     203           0 :     debug_printf("-----------------------------------------------------\n");
     204           0 : }
     205             : 
     206             : 
     207             : //-----------------------------------------------------------------------------
     208             : 
     209          20 : std::string StringForSeconds(double seconds)
     210             : {
     211          20 :     double scale = 1e6;
     212          20 :     const char* unit = " us";
     213          20 :     if(seconds > 1.0)
     214           0 :         scale = 1, unit = " s";
     215          20 :     else if(seconds > 1e-3)
     216          11 :         scale = 1e3, unit = " ms";
     217             : 
     218          40 :     std::stringstream ss;
     219          20 :     ss << seconds*scale;
     220          20 :     ss << unit;
     221          40 :     return ss.str();
     222             : }
     223             : 
     224             : 
     225           0 : std::string StringForCycles(Cycles cycles)
     226             : {
     227           0 :     double scale = 1.0;
     228           0 :     const char* unit = " c";
     229           0 :     if(cycles > 10000000000LL)   // 10 Gc
     230           0 :         scale = 1e-9, unit = " Gc";
     231           0 :     else if(cycles > 10000000)   // 10 Mc
     232           0 :         scale = 1e-6, unit = " Mc";
     233           0 :     else if(cycles > 10000)  // 10 kc
     234           0 :         scale = 1e-3, unit = " kc";
     235             : 
     236           0 :     std::stringstream ss;
     237           0 :     ss << cycles*scale;
     238           0 :     ss << unit;
     239           0 :     return ss.str();
     240           3 : }

Generated by: LCOV version 1.13