-
Notifications
You must be signed in to change notification settings - Fork 0
/
Ticks.h
342 lines (285 loc) · 16 KB
/
Ticks.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
* Copyright (c) 2011 Mellanox Technologies Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the Mellanox Technologies Ltd nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
*/
#ifndef TICKS_H_
#define TICKS_H_
/*
* TicksTime represents a point in time measured by ticks (related to some
* abstract base point and not necessarily to real clock)
* TicksDuration represents an interval of time measured by ticks (usually
* delta between 2 TicksTime objects) that can be converted to nsec
* The classes hide their internal implementation and therefore free to optimize/change it.
* The current known internal implementations are based on clock_gettime or on RDTSC register.
* Both classes are intended to be as efficient as primitive integral data types.
* The advantage in these classes is because:
* 1. you can easily switch your application to be based on clock_gettime or
* on RDTSC register (or on any other future implementation)
* 2. the classes contain many utility and conversion functions to/from many 'time' types
* 3. the classes contain operators for all valid comparison & arithmetic
* operations that are *legal* for them
* 4. any operation that is illegal will be recognized as compilation
* error (for example division by TicksTime object)
* 5, using these classes will make your code cleaner and safer and faster
* 6. and more...
*
------------
NOTE #1:
For efficiency purposes, the classes leave all runtime checks to their users.
Hence, there are no overflow/underflow/divizion-by-zero checks. Also there is no
semantic for illegal values (for example: zero/negative time is considered legal
from the class's point of view)
(still, there are many compile time error checks, based on types and based on valid operations)
NOTE #2:
any method that is related to getting current time * from CLOCK (not from RDTSC register) * may
throw Exception (in case of fatal error related to clock)
------------
------------
Inspired by Boost.Date_Time documentation,
this section describes some of basic arithmetic rules that can be performed with TicksTime
and TicksDuration objects:
TicksTime + TicksDuration --> TicksTime
TicksTime - TicksDuration --> TicksTime
TicksTime - TicksTime --> TicksDuration
Unlike regular numeric types, the following operations are undefined and therefore prevented
by the class'es interface:
TicksTime + TicksTime --> Undefined
TicksDuration + TicksTime --> Undefined
TicksDuration - TicksTime --> Undefined
---
TicksDuration represent a length of time and can have positive and negative values. It is
frequently useful to be able to perform calculations with other TicksDuration objects and
with simple integral values. The following describes these calculations:
TicksDuration + TicksDuration --> TicksDuration
TicksDuration - TicksDuration --> TicksDuration
TicksDuration * Integer --> TicksDuration
Integer * TicksDuration --> TicksDuration
TicksDuration / Integer --> TicksDuration (Integer Division rules)
*/
#include <time.h> /* clock_gettime()*/
#include <exception>
#include <stdint.h>// for int64_t
#include <stdlib.h>// for qsort
#include "ticks_os.h"
// usefull constants
static const int64_t USEC_IN_SEC = 1000*1000;
static const int64_t NSEC_IN_MSEC = 1000*1000;
//------------------------------------------------------------------------------
// utility functions
inline static int64_t timeval2nsec (const struct timeval &_val) { return NSEC_IN_SEC*_val.tv_sec + 1000*_val.tv_usec; }
ticks_t get_tsc_rate_per_second();
//------------------------------------------------------------------------------
// forward declaration of classes in this file
class TicksImpl;
class TicksImplRdtsc;
class TicksImplClock;
//
class TicksBase;
class TicksTime;
class TicksDuration;
//------------------------------------------------------------------------------
class TicksImpl {
friend class TicksBase;
protected:
TicksImpl() {}
virtual ~TicksImpl() {}
/* commented out because we avoid inheritance for saving time of dynamically invoking virtual function
virtual ticks_t nsec2ticks(int64_t _val) = 0;
virtual int64_t ticks2nsec(ticks_t _val) = 0;
virtual ticks_t getCurrentTicks( ) = 0;
*/
};
//------------------------------------------------------------------------------
class TicksImplRdtsc : public TicksImpl {
friend class TicksBase;
friend class TicksTime;
protected:
TicksImplRdtsc() {}
virtual ~TicksImplRdtsc() {}
static ticks_t nsec2ticks(int64_t _val) {if (_val < MAX_MSEC_CONVERT) return nsec2ticks_smallNamubers(_val); else return (ticks_t)_val/NSEC_IN_MSEC*TICKS_PER_MSEC + nsec2ticks_smallNamubers(_val%NSEC_IN_MSEC);}
static int64_t ticks2nsec(ticks_t _val) {if (_val < MAX_MSEC_CONVERT) return ticks2nsec_smallNamubers(_val); else return (int64_t)_val/TICKS_PER_MSEC*NSEC_IN_MSEC + ticks2nsec_smallNamubers(_val%TICKS_PER_MSEC);}
static ticks_t getCurrentTicks() {return os_gettimeoftsc() - BASE_TICKS;}
//these methods may overflow in conversions for values greater than 1 million seconds ( > 10 days)
inline static ticks_t nsec2ticks_smallNamubers(int64_t _val) {return (ticks_t)_val*TICKS_PER_MSEC/NSEC_IN_MSEC;}
inline static int64_t ticks2nsec_smallNamubers(ticks_t _val) {return (int64_t)_val*NSEC_IN_MSEC/TICKS_PER_MSEC;}
static const int64_t TICKS_PER_SEC;
static const int64_t TICKS_PER_MSEC;
static const int64_t MAX_MSEC_CONVERT;
static const ticks_t BASE_TICKS;
};
//------------------------------------------------------------------------------
class TicksImplClock : public TicksImpl {
friend class TicksBase;
protected:
TicksImplClock() {}
virtual ~TicksImplClock() {}
static ticks_t nsec2ticks(int64_t _val) {return (ticks_t) _val;}
static int64_t ticks2nsec(ticks_t _val) {return (int64_t) _val;}
static ticks_t getCurrentTicks() {return os_gettimeofclock();}
//------------------------------------------------------------------------------
//utility function for throwing exception related to this class (no need to inline it)
static void doThrow(const char *_func, const char *_file, int _line) throw(std::exception);
};
//==============================================================================
//==============================================================================
//==============================================================================
class TicksBase{
public:
enum Mode {RDTSC, CLOCK};
// initialize our system to rdtsc mode or clock mode
// getting time from RDTSC takes ~34nsec, getting time from CLOCK takes ~620nsec
// (in addition clock accuracy is 1usec)
static bool init(Mode _mode = RDTSC);
//------------------------------------------------------------------------------
protected:
static Mode ms_mode;
ticks_t m_ticks;
//------------------------------------------------------------------------------
// these 3 methods are the sole thing that depends on mode (RDTSC/CLOCK/...)
inline static ticks_t nsec2ticks(int64_t _val) {return ms_mode == RDTSC ? TicksImplRdtsc::nsec2ticks(_val) : TicksImplClock::nsec2ticks(_val);}
static ticks_t nsec2ticksNonInline(int64_t _val);
inline static int64_t ticks2nsec(ticks_t _val) {return ms_mode == RDTSC ? TicksImplRdtsc::ticks2nsec(_val) : TicksImplClock::ticks2nsec(_val);}
inline static ticks_t getCurrentTicks( ) {return ms_mode == RDTSC ? TicksImplRdtsc::getCurrentTicks() : TicksImplClock::getCurrentTicks();}
//------------------------------------------------------------------------------
// CTORs
inline TicksBase (ticks_t _ticks) : m_ticks(_ticks){}
TicksBase (ticks_t _ticks, int); //provide non inline CTOR just to quite compiler
inline TicksBase (const TicksBase & other) : m_ticks(other.m_ticks){}
};
//==============================================================================
class TicksDuration : public TicksBase{
friend class TicksTime;
public:
// CTORs & DTOR
inline TicksDuration () : TicksBase(0){}
inline explicit TicksDuration (int64_t _nsec) : TicksBase(nsec2ticks(_nsec)){}
inline explicit TicksDuration (const struct timeval & _val) : TicksBase(nsec2ticks(timeval2nsec(_val))){}
inline explicit TicksDuration (const struct timespec & _val) : TicksBase(nsec2ticks(timespec2nsec(_val))){}
inline ~TicksDuration(){}
//Useful constants
static const TicksDuration TICKS0; // = TicksDuration(0);
static const TicksDuration TICKS1USEC;
static const TicksDuration TICKS1MSEC;
static const TicksDuration TICKS1SEC;
static const TicksDuration TICKS1MIN; // = TicksDuration(TICKS1SEC*60);
static const TicksDuration TICKS1HOUR; // = TicksDuration(TICKS1MIN*60);
static const TicksDuration TICKS1DAY; // = TicksDuration(TICKS1HOUR*24);
static const TicksDuration TICKS1WEEK; // = TicksDuration(TICKS1DAY*7);
//operators
inline bool operator< (const TicksDuration & rhs) const {return this->m_ticks < rhs.m_ticks;}
inline bool operator> (const TicksDuration & rhs) const {return this->m_ticks > rhs.m_ticks;}
inline bool operator== (const TicksDuration & rhs) const {return this->m_ticks == rhs.m_ticks;}
inline bool operator!= (const TicksDuration & rhs) const {return this->m_ticks != rhs.m_ticks;}
inline bool operator<= (const TicksDuration & rhs) const {return this->m_ticks <= rhs.m_ticks;}
inline bool operator>= (const TicksDuration & rhs) const {return this->m_ticks >= rhs.m_ticks;}
inline TicksDuration operator+ (const TicksDuration & rhs) const {return TicksDuration(this->m_ticks + rhs.m_ticks, true);}
inline TicksDuration operator- (const TicksDuration & rhs) const {return TicksDuration(this->m_ticks - rhs.m_ticks, true);}
inline TicksDuration & operator+= (const TicksDuration & rhs) {this->m_ticks += rhs.m_ticks; return *this;}
inline TicksDuration & operator-= (const TicksDuration & rhs) {this->m_ticks -= rhs.m_ticks; return *this;}
inline TicksDuration operator* (int rhs) const {return TicksDuration(this->m_ticks * rhs, true);}
inline TicksDuration operator/ (int rhs) const {return TicksDuration(this->m_ticks / rhs, true);}
inline TicksDuration & operator*= (int rhs) {this->m_ticks *= rhs; return *this;}
inline TicksDuration & operator/= (int rhs) {this->m_ticks /= rhs; return *this;}
// 'getCurrentTicks' related method - may throw exception if using clock_gettime and call fatally failed
/*inline*/ void setDurationSince(const TicksTime & start);
inline void setFromSeconds(double seconds) {
int64_t nsec = (int64_t)(seconds * NSEC_IN_SEC);
m_ticks = nsec2ticks(nsec);
}
// utility functions
//------------------------------------------------------------------------------
inline int64_t toNsec() const { return ticks2nsec(m_ticks); }
inline int64_t toUsec() const { return (toNsec()+500)/1000; }
inline double toDecimalUsec() const { return (double)toNsec()/1000; }
//------------------------------------------------------------------------------
inline void toTimespec(struct timespec &_val) const {
int64_t nsec = toNsec();
_val.tv_sec = (long)(nsec / NSEC_IN_SEC);
_val.tv_nsec = (long)(nsec % NSEC_IN_SEC);
}
//------------------------------------------------------------------------------
inline void toTimeval(struct timeval &_val) const {
int64_t usec = toUsec();
_val.tv_sec = (long)(usec / (USEC_IN_SEC));
_val.tv_usec = (long)(usec % (USEC_IN_SEC));
}
//------------------------------------------------------------------------------
static int compare(const void *arg1, const void *arg2) {
const TicksDuration & t1 = *(TicksDuration*)arg1;
const TicksDuration & t2 = *(TicksDuration*)arg2;
return t1 > t2 ? 1 : t1 < t2 ? -1 : 0; // Note: returning t1-t2 will not be safe because of cast to int
}
//------------------------------------------------------------------------------
static void sort(TicksDuration* pArr, size_t size) {
qsort(pArr, size, sizeof(TicksDuration), compare);
}
//------------------------------------------------------------------------------
static TicksDuration stdDev(TicksDuration* pArr, size_t size);
private:
inline explicit TicksDuration(ticks_t _ticks, bool): TicksBase(_ticks){}
explicit TicksDuration (int64_t _nsec, int); // provide non inline function for reducing code size outside fast path
};
//==============================================================================
class TicksTime : public TicksBase{
friend class TicksDuration;
public:
//CTORs & DTOR
inline TicksTime () : TicksBase(0){} // init to 0
//inline TicksTime (bool) : TicksBase(getCurrentTicks()){} //init to now
inline ~TicksTime(){}
static const TicksTime TICKS0; // = TicksTime(0);
// 'getCurrentTicks' related method - may throw exception if using clock_gettime and call fatally failed
inline static TicksTime now() {return TicksTime(getCurrentTicks());}
inline TicksTime & setNow() {m_ticks = getCurrentTicks(); return *this;}
TicksTime & setNowNonInline(); // provide non inline function for reducing code size outside fast path
inline TicksDuration durationTillNow() {return TicksDuration( getCurrentTicks() - m_ticks, true );}
//operators
inline bool operator< (const TicksTime & rhs) const {return this->m_ticks < rhs.m_ticks;}
inline bool operator> (const TicksTime & rhs) const {return this->m_ticks > rhs.m_ticks;}
inline bool operator== (const TicksTime & rhs) const {return this->m_ticks == rhs.m_ticks;}
inline bool operator!= (const TicksTime & rhs) const {return this->m_ticks != rhs.m_ticks;}
inline bool operator<= (const TicksTime & rhs) const {return this->m_ticks <= rhs.m_ticks;}
inline bool operator>= (const TicksTime & rhs) const {return this->m_ticks >= rhs.m_ticks;}
inline TicksTime operator+ (const TicksDuration & rhs) const {return TicksTime (this->m_ticks + rhs.m_ticks);}
inline TicksTime operator- (const TicksDuration & rhs) const {return TicksTime (this->m_ticks - rhs.m_ticks);}
inline TicksDuration operator- (const TicksTime & rhs) const {return TicksDuration(this->m_ticks - rhs.m_ticks, true);}
inline TicksTime & operator+= (const TicksDuration & rhs) {this->m_ticks += rhs.m_ticks; return *this;}
inline TicksTime & operator-= (const TicksDuration & rhs) {this->m_ticks -= rhs.m_ticks; return *this;}
// this method is for debug purposes only since it breaks the concept of TicksTime as a point in time
// and consider it as duration since some base point!!
inline int64_t debugToNsec() const { return ticks2nsec(m_ticks);}
//------------------------------------------------------------------------------
private:
inline TicksTime(ticks_t _ticks): TicksBase(_ticks){}
};
//------------------------------------------------------------------------------
// 'getCurrentTicks' related method - may throw exception if using clock_gettime and call fatally failed
inline void TicksDuration::setDurationSince(const TicksTime & start) {
m_ticks = getCurrentTicks();
m_ticks -= start.m_ticks;
}
#endif /* TICKS_H_ */