Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
timer.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 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 <sstream> // std::stringstream
31 #include <numeric>
32 #include <cmath>
33 #include <cfloat>
34 #include <cstdarg>
35 
36 #include "lib/module_init.h"
38 #include "lib/posix/posix_time.h"
39 # include "lib/sysdep/cpu.h"
40 #if OS_WIN
42 #endif
43 #if OS_UNIX
44 # include <unistd.h>
45 #endif
46 
47 #if OS_UNIX || OS_WIN
48 # define HAVE_GETTIMEOFDAY 1
49 #else
50 # define HAVE_GETTIMEOFDAY 0
51 #endif
52 
53 #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN
54 # define HAVE_CLOCK_GETTIME 1
55 #else
56 # define HAVE_CLOCK_GETTIME 0
57 #endif
58 
59 // rationale for wrapping gettimeofday and clock_gettime, instead of just
60 // emulating them where not available: allows returning higher-resolution
61 // timer values than their us / ns interface, via double [seconds].
62 // they're also not guaranteed to be monotonic.
63 
64 #if HAVE_CLOCK_GETTIME
65 static struct timespec start;
66 #elif HAVE_GETTIMEOFDAY
67 static struct timeval start;
68 #endif
69 
70 
71 //-----------------------------------------------------------------------------
72 // timer API
73 
75 {
76 #if OS_WIN
77  // whrt_Time starts at zero, nothing needs to be done.
78 #elif HAVE_CLOCK_GETTIME
79  (void)clock_gettime(CLOCK_REALTIME, &start);
80 #elif HAVE_GETTIMEOFDAY
81  gettimeofday(&start, 0);
82 #endif
83 }
84 
86 // NB: does not guarantee strict monotonicity - callers must avoid
87 // dividing by the difference of two equal times.
88 static void EnsureMonotonic(double& newTime)
89 {
91  static double maxTime;
92  maxTime = std::max(maxTime, newTime);
93  newTime = maxTime;
95 }
96 
97 
98 double timer_Time()
99 {
100  double t;
101 
102 #if OS_WIN
103  t = whrt_Time();
104 #elif HAVE_CLOCK_GETTIME
105  ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first
106  struct timespec cur;
107  (void)clock_gettime(CLOCK_REALTIME, &cur);
108  t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9;
109 #elif HAVE_GETTIMEOFDAY
110  ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first
111  struct timeval cur;
112  gettimeofday(&cur, 0);
113  t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
114 #else
115 # error "timer_Time: add timer implementation for this platform!"
116 #endif
117 
118  EnsureMonotonic(t);
119  return t;
120 }
121 
122 
123 // cached because the default implementation may take several milliseconds
124 static double resolution;
125 
127 {
128 #if OS_WIN
130 #elif HAVE_CLOCK_GETTIME
131  struct timespec ts;
132  if(clock_getres(CLOCK_REALTIME, &ts) == 0)
133  resolution = ts.tv_nsec * 1e-9;
134 #else
135  const double t0 = timer_Time();
136  double t1, t2;
137  do t1 = timer_Time(); while(t1 == t0);
138  do t2 = timer_Time(); while(t2 == t1);
139  resolution = t2-t1;
140 #endif
141 
142  return INFO::OK;
143 }
144 
146 {
147  static ModuleInitState initState;
148  ModuleInit(&initState, InitResolution);
149  return resolution;
150 }
151 
152 
153 //-----------------------------------------------------------------------------
154 // client API
155 
156 // intrusive linked-list of all clients. a fixed-size limit would be
157 // acceptable (since timers are added manually), but the list is easy
158 // to implement and only has the drawback of exposing TimerClient to users.
159 //
160 // do not use std::list et al. for this! we must be callable at any time,
161 // especially before NLSO ctors run or before heap init.
162 static size_t numClients;
164 
165 
166 TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description)
167 {
168  tc->sum.SetToZero();
169 
170  tc->description = description;
171 
172  // insert at front of list
173  tc->next = clients;
174  clients = tc;
175  numClients++;
176 
177  return tc;
178 }
179 
180 
182 {
183  debug_printf(L"TIMER TOTALS (%lu clients)\n", (unsigned long)numClients);
184  debug_printf(L"-----------------------------------------------------\n");
185 
186  while(clients)
187  {
188  // (make sure list and count are consistent)
189  ENSURE(numClients != 0);
190  TimerClient* tc = clients;
191  clients = tc->next;
192  numClients--;
193 
194  const std::wstring duration = tc->sum.ToString();
195  debug_printf(L" %ls: %ls (%lux)\n", tc->description, duration.c_str(), (unsigned long)tc->num_calls);
196  }
197 
198  debug_printf(L"-----------------------------------------------------\n");
199 }
200 
201 
202 //-----------------------------------------------------------------------------
203 
204 std::wstring StringForSeconds(double seconds)
205 {
206  double scale = 1e6;
207  const wchar_t* unit = L" us";
208  if(seconds > 1.0)
209  scale = 1, unit = L" s";
210  else if(seconds > 1e-3)
211  scale = 1e3, unit = L" ms";
212 
213  std::wstringstream ss;
214  ss << seconds*scale;
215  ss << unit;
216  return ss.str();
217 }
218 
219 
220 std::wstring StringForCycles(Cycles cycles)
221 {
222  double scale = 1.0;
223  const wchar_t* unit = L" c";
224  if(cycles > 10000000000LL) // 10 Gc
225  scale = 1e-9, unit = L" Gc";
226  else if(cycles > 10000000) // 10 Mc
227  scale = 1e-6, unit = L" Mc";
228  else if(cycles > 10000) // 10 kc
229  scale = 1e-3, unit = L" kc";
230 
231  std::wstringstream ss;
232  ss << cycles*scale;
233  ss << unit;
234  return ss.str();
235 }
static pthread_mutex_t ensure_monotonic_mutex
Definition: timer.cpp:85
void * pthread_mutex_t
Definition: wpthread.h:82
static TimerClient * clients
Definition: timer.cpp:163
const Status OK
Definition: status.h:386
static double resolution
Definition: timer.cpp:124
int clock_gettime(clockid_t clock, struct timespec *ts)
Definition: wtime.cpp:121
TimerClient * next
Definition: timer.h:281
TimerClient * timer_AddClient(TimerClient *tc, const wchar_t *description)
make the given TimerClient (usually instantiated as static data) ready for use.
Definition: timer.cpp:166
static ModuleInitState initState
Definition: h_mgr.cpp:742
time_t tv_sec
Definition: wtime.h:64
static size_t numClients
Definition: timer.cpp:162
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
long tv_nsec
Definition: wtime.h:65
static Status InitResolution()
Definition: timer.cpp:126
double whrt_Resolution()
Definition: whrt.cpp:143
void timer_LatchStartTime()
timer_Time will subsequently return values relative to the current time.
Definition: timer.cpp:74
int pthread_mutex_lock(pthread_mutex_t *m)
Definition: wpthread.cpp:329
i64 Status
Error handling system.
Definition: status.h:171
TimerUnit sum
Definition: timer.h:276
double timer_Time()
Definition: timer.cpp:98
double whrt_Time()
Definition: whrt.cpp:208
double timer_Resolution()
Definition: timer.cpp:145
i64 Cycles
Definition: timer.h:58
const wchar_t * description
Definition: timer.h:279
int pthread_mutex_unlock(pthread_mutex_t *m)
Definition: wpthread.cpp:347
int clock_getres(clockid_t clock, struct timespec *ts)
Definition: wtime.cpp:131
Definition: wtime.h:62
void SetToZero()
Definition: timer.h:220
#define PTHREAD_MUTEX_INITIALIZER
Definition: wpthread.h:84
std::wstring ToString() const
Definition: timer.h:254
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40
std::wstring StringForSeconds(double seconds)
internal helper functions for returning an easily readable string (i.e.
Definition: timer.cpp:204
std::wstring StringForCycles(Cycles cycles)
Definition: timer.cpp:220
intptr_t num_calls
Definition: timer.h:285
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
static void EnsureMonotonic(double &newTime)
Definition: timer.cpp:88
void timer_DisplayClientTotals()
display all clients&#39; totals; does not reset them.
Definition: timer.cpp:181