SkePU  1.2
 All Classes Namespaces Files Functions Variables Enumerations Friends Macros Groups Pages
timer.h
1 #ifndef SKEPU_TIMING_H___
2 #define SKEPU_TIMING_H___
3 
4 #include <sys/time.h>
5 #include <unistd.h>
6 #include <stdint.h>
7 #include <vector>
8 #include <cmath>
9 
10 #define HAVE_CLOCK_GETTIME
11 // #define CLOCK_MONOTONIC
12 
13 
14 namespace skepu
15 {
16 
17 
18 
19 class Timer
20 {
21 public:
22  void start()
23  {
24  skepu_clock_gettime(&start_ts);
25  }
26  void stop()
27  {
28  skepu_clock_gettime(&stop_ts);
29  m_data.push_back(skepu_timing_timespec_delay_us(&start_ts, &stop_ts));
30  }
31  double getAverageTime()
32  {
33  if(m_data.empty())
34  return 0;
35 
36  return (getTotalTime()/m_data.size());
37  }
38  double getTotalTime()
39  {
40  double totTime = 0.0;
41  for(int i=0; i<m_data.size(); ++i)
42  {
43  totTime += m_data[i];
44  }
45  return totTime;
46  }
47 
48  Timer()
49  {
50  skepu_timing_init();
51  }
52 
53  void reset()
54  {
55  m_data.clear();
56  }
57 private:
58 
59  void skepu_timing_init(void);
60  void skepu_clock_gettime(struct timespec *ts);
61 
62  std::vector<double> m_data;
63 
64  struct timespec start_ts;
65  struct timespec stop_ts;
66  struct timespec skepu_reference_start_time_ts;
67 
69  void skepu_timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *result)
70  {
71  result->tv_sec = a->tv_sec - b->tv_sec;
72  result->tv_nsec = a->tv_nsec - b->tv_nsec;
73 
74  if ((result)->tv_nsec < 0)
75  {
76  --(result)->tv_sec;
77  result->tv_nsec += 1000000000;
78  }
79  }
80 
81 
82  /* Returns the time elapsed between start and end in microseconds */
83  double skepu_timing_timespec_delay_us(struct timespec *start, struct timespec *end)
84  {
85  struct timespec diff;
86 
87  skepu_timespec_sub(end, start, &diff);
88 
89  double us = (diff.tv_sec*1e6) + (diff.tv_nsec*1e-3);
90 
91  return us;
92  }
93 
94  double skepu_timing_timespec_to_us(struct timespec *ts)
95  {
96  return (1000000.0*ts->tv_sec) + (0.001*ts->tv_nsec);
97  }
98 
99  double skepu_timing_now(void)
100  {
101  struct timespec now;
102  skepu_clock_gettime(&now);
103 
104  return skepu_timing_timespec_to_us(&now);
105  }
106 };
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
122 
123 #include <time.h>
124 #ifndef _POSIX_C_SOURCE
125 /* for clock_gettime */
126 #define _POSIX_C_SOURCE 199309L
127 #endif
128 
129 #ifdef __linux__
130 #ifndef CLOCK_MONOTONIC_RAW
131 #define CLOCK_MONOTONIC_RAW 4
132 #endif
133 #endif
134 
135 static struct timespec skepu_reference_start_time_ts;
136 
137 /* Modern CPUs' clocks are usually not synchronized so we use a monotonic clock
138 * to have consistent timing measurements. The CLOCK_MONOTONIC_RAW clock is not
139 * subject to NTP adjustments, but is not available on all systems (in that
140 * case we use the CLOCK_MONOTONIC clock instead). */
141 static void skepu_clock_readtime(struct timespec *ts)
142 {
143 #ifdef CLOCK_MONOTONIC_RAW
144  static int raw_supported = 0;
145  switch (raw_supported)
146  {
147  case -1:
148  break;
149  case 1:
150  clock_gettime(CLOCK_MONOTONIC_RAW, ts);
151  return;
152  case 0:
153  if (clock_gettime(CLOCK_MONOTONIC_RAW, ts))
154  {
155  raw_supported = -1;
156  break;
157  }
158  else
159  {
160  raw_supported = 1;
161  return;
162  }
163  }
164 #endif
165  clock_gettime(CLOCK_MONOTONIC, ts);
166 }
167 
168 void Timer::skepu_timing_init(void)
169 {
170  skepu_clock_gettime(&skepu_reference_start_time_ts);
171 }
172 
173 void Timer::skepu_clock_gettime(struct timespec *ts)
174 {
175  struct timespec absolute_ts;
176 
177  /* Read the current time */
178  skepu_clock_readtime(&absolute_ts);
179 
180  /* Compute the relative time since initialization */
181  skepu_timespec_sub(&absolute_ts, &skepu_reference_start_time_ts, ts);
182 }
183 
184 #else // !HAVE_CLOCK_GETTIME
185 
186 union skepu_u_tick
187 {
188  uint64_t tick;
189 
190  struct
191  {
192  uint32_t low;
193  uint32_t high;
194  }
195  sub;
196 };
197 
198 #define SKEPU_MIN(a,b) ((a)<(b)?(a):(b))
199 
200 #define SKEPU_GET_TICK(t) __asm__ volatile("rdtsc" : "=a" ((t).sub.low), "=d" ((t).sub.high))
201 #define SKEPU_TICK_RAW_DIFF(t1, t2) ((t2).tick - (t1).tick)
202 #define SKEPU_TICK_DIFF(t1, t2) (SKEPU_TICK_RAW_DIFF(t1, t2) - skepu_residual)
203 
204 static union skepu_u_tick skepu_reference_start_tick;
205 static double skepu_scale = 0.0;
206 static unsigned long long skepu_residual = 0;
207 
208 static int skepu_inited = 0;
209 
210 void Tuner::skepu_timing_init(void)
211 {
212  static union skepu_u_tick t1, t2;
213  int i;
214 
215  if (skepu_inited) return;
216 
217  skepu_residual = (unsigned long long)1 << 63;
218 
219  for(i = 0; i < 20; i++)
220  {
221  SKEPU_GET_TICK(t1);
222  SKEPU_GET_TICK(t2);
223  skepu_residual = SKEPU_MIN(skepu_residual, SKEPU_TICK_RAW_DIFF(t1, t2));
224  }
225 
226  {
227  struct timeval tv1,tv2;
228 
229  SKEPU_GET_TICK(t1);
230  gettimeofday(&tv1,0);
231  usleep(500000);
232  SKEPU_GET_TICK(t2);
233  gettimeofday(&tv2,0);
234  skepu_scale = ((tv2.tv_sec*1e6 + tv2.tv_usec) -
235  (tv1.tv_sec*1e6 + tv1.tv_usec)) /
236  (double)(SKEPU_TICK_DIFF(t1, t2));
237  }
238 
239  SKEPU_GET_TICK(skepu_reference_start_tick);
240 
241  skepu_inited = 1;
242 }
243 
244 void Timer::skepu_clock_gettime(struct timespec *ts)
245 {
246  union skepu_u_tick tick_now;
247 
248  SKEPU_GET_TICK(tick_now);
249 
250  uint64_t elapsed_ticks = SKEPU_TICK_DIFF(skepu_reference_start_tick, tick_now);
251 
252  /* We convert this number into nano-seconds so that we can fill the
253  * timespec structure. */
254  uint64_t elapsed_ns = (uint64_t)(((double)elapsed_ticks)*(skepu_scale*1000.0));
255 
256  long tv_nsec = (elapsed_ns % 1000000000);
257  time_t tv_sec = (elapsed_ns / 1000000000);
258 
259  ts->tv_sec = tv_sec;
260  ts->tv_nsec = tv_nsec;
261 }
262 
263 
264 #endif // HAVE_CLOCK_GETTIME
265 
266 
267 
268 
269 
270 } // end namespace skepu
271 
272 
273 
274 
275 
276 #endif
277