Line | Branch | Exec | Source |
---|---|---|---|
1 | /*********************************************************************************/ | ||
2 | /* Copyright 2009-2021 Barcelona Supercomputing Center */ | ||
3 | /* */ | ||
4 | /* This file is part of the DLB library. */ | ||
5 | /* */ | ||
6 | /* DLB is free software: you can redistribute it and/or modify */ | ||
7 | /* it under the terms of the GNU Lesser General Public License as published by */ | ||
8 | /* the Free Software Foundation, either version 3 of the License, or */ | ||
9 | /* (at your option) any later version. */ | ||
10 | /* */ | ||
11 | /* DLB is distributed in the hope that it will be useful, */ | ||
12 | /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ | ||
13 | /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ | ||
14 | /* GNU Lesser General Public License for more details. */ | ||
15 | /* */ | ||
16 | /* You should have received a copy of the GNU Lesser General Public License */ | ||
17 | /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */ | ||
18 | /*********************************************************************************/ | ||
19 | |||
20 | #include "support/mytime.h" | ||
21 | |||
22 | #include "support/debug.h" | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | #include <math.h> | ||
27 | |||
28 | enum { MS_PER_SECOND = 1000LL }; | ||
29 | enum { US_PER_SECOND = 1000000LL }; | ||
30 | enum { NS_PER_SECOND = 1000000000LL }; | ||
31 | |||
32 | 3399 | void get_time( struct timespec *t ) { | |
33 | 3399 | clock_gettime( CLOCK_MONOTONIC, t); | |
34 | 3399 | } | |
35 | |||
36 | 97 | void get_time_coarse( struct timespec *t ) { | |
37 | #ifdef CLOCK_MONOTONIC_COARSE | ||
38 | 97 | clock_gettime( CLOCK_MONOTONIC_COARSE, t); | |
39 | #else | ||
40 | clock_gettime( CLOCK_MONOTONIC, t); | ||
41 | #endif | ||
42 | 97 | } | |
43 | |||
44 | 438 | void get_time_real(struct timespec *t) { | |
45 | /* Avoid using CLOCK_REALTIME except for pthread_mutex_timedlock, | ||
46 | * which does not work with MONOTONIC */ | ||
47 | 438 | clock_gettime(CLOCK_REALTIME, t); | |
48 | 438 | } | |
49 | |||
50 | 3242 | int64_t get_time_in_ns(void) { | |
51 | struct timespec t; | ||
52 | 3242 | get_time(&t); | |
53 | 3242 | return to_nsecs(&t); | |
54 | } | ||
55 | |||
56 | 9 | int diff_time( struct timespec init, struct timespec end, struct timespec* diff ) { | |
57 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9 | if ( init.tv_sec > end.tv_sec ) { |
58 | 2 | return -1; | |
59 | } else { | ||
60 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
7 | if ( ( init.tv_sec == end.tv_sec ) && ( init.tv_nsec > end.tv_nsec ) ) { |
61 | 1 | return -1; | |
62 | } else { | ||
63 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if ( init.tv_nsec > end.tv_nsec ) { |
64 | 2 | diff->tv_sec = end.tv_sec - ( init.tv_sec + 1 ); | |
65 | 2 | diff->tv_nsec = ( end.tv_nsec + NS_PER_SECOND ) - init.tv_nsec; | |
66 | } else { | ||
67 | 4 | diff->tv_sec = end.tv_sec - init.tv_sec; | |
68 | 4 | diff->tv_nsec = end.tv_nsec - init.tv_nsec; | |
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | 6 | return 0; | |
74 | } | ||
75 | |||
76 | 2 | void add_time( struct timespec t1, struct timespec t2, struct timespec* sum ) { | |
77 | 2 | sum->tv_nsec = t1.tv_nsec + t2.tv_nsec; | |
78 | |||
79 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if ( sum->tv_nsec >= NS_PER_SECOND ) { |
80 | 1 | sum->tv_sec = t1.tv_sec + t2.tv_sec + ( sum->tv_nsec/NS_PER_SECOND ); | |
81 | 1 | sum->tv_nsec = ( sum->tv_nsec % NS_PER_SECOND ); | |
82 | } else { | ||
83 | 1 | sum->tv_sec = t1.tv_sec + t2.tv_sec; | |
84 | } | ||
85 | 2 | } | |
86 | |||
87 | 2 | void mult_time( struct timespec t1, int factor, struct timespec* prod ) { | |
88 | 2 | int64_t nsec = (int64_t)t1.tv_nsec * factor; | |
89 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (nsec >= NS_PER_SECOND) { |
90 | 1 | prod->tv_sec = t1.tv_sec * factor + nsec / NS_PER_SECOND; | |
91 | 1 | prod->tv_nsec = nsec % NS_PER_SECOND; | |
92 | } else { | ||
93 | 1 | prod->tv_nsec = nsec; | |
94 | 1 | prod->tv_sec = t1.tv_sec * factor; | |
95 | } | ||
96 | 2 | } | |
97 | |||
98 | ✗ | void diff_time_mult(struct timespec* time, int mult_factor, struct timespec * result){ | |
99 | ✗ | *result = *time; | |
100 | ✗ | clock_gettime(CLOCK_REALTIME, time); | |
101 | ✗ | diff_time(*result,*time , result); | |
102 | ✗ | mult_time(*result,mult_factor,result); | |
103 | } | ||
104 | |||
105 | 1 | void reset( struct timespec *t1 ) { | |
106 | 1 | t1->tv_nsec = 0; | |
107 | 1 | t1->tv_sec = 0; | |
108 | 1 | } | |
109 | |||
110 | 12 | double to_secs( struct timespec t1 ) { | |
111 | 12 | return t1.tv_sec + (double)t1.tv_nsec / NS_PER_SECOND; | |
112 | } | ||
113 | |||
114 | 3248 | int64_t to_nsecs( const struct timespec *ts ) { | |
115 | 3248 | return ts->tv_nsec + (int64_t)ts->tv_sec * NS_PER_SECOND; | |
116 | } | ||
117 | |||
118 | 22 | double nsecs_to_secs( int64_t nsecs ) { | |
119 | 22 | return (double)nsecs / NS_PER_SECOND; | |
120 | } | ||
121 | |||
122 | // Return timeval diff in us | ||
123 | 2 | int64_t timeval_diff( const struct timeval *init, const struct timeval *end ) { | |
124 | 4 | return (int64_t)(end->tv_sec - init->tv_sec) * US_PER_SECOND + | |
125 | 2 | (int64_t)(end->tv_usec - init->tv_usec); | |
126 | } | ||
127 | |||
128 | // Return timespec diff in ns | ||
129 | 93 | int64_t timespec_diff( const struct timespec *init, const struct timespec *end ) { | |
130 | 186 | return (int64_t)(end->tv_sec - init->tv_sec) * NS_PER_SECOND + | |
131 | 93 | (int64_t)(end->tv_nsec - init->tv_nsec); | |
132 | } | ||
133 | |||
134 | 2 | void add_tv_to_ts( const struct timeval *t1, const struct timeval *t2, | |
135 | struct timespec *res ) { | ||
136 | 2 | long sec = t2->tv_sec + t1->tv_sec; | |
137 | 2 | long nsec = (t2->tv_usec + t1->tv_usec) * MS_PER_SECOND; | |
138 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (nsec >= NS_PER_SECOND) { |
139 | 1 | nsec -= NS_PER_SECOND; | |
140 | 1 | sec++; | |
141 | } | ||
142 | 2 | res->tv_sec = sec; | |
143 | 2 | res->tv_nsec = nsec; | |
144 | 2 | } | |
145 | |||
146 | 35 | void ns_to_human( char *buf, size_t size, int64_t ns ) { | |
147 |
4/4✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 33 times.
|
35 | if (!buf || !size) return; |
148 | |||
149 | 33 | const char* const units[] = {"ns", "us", "ms", "s"}; | |
150 | 33 | int decimal_part = 0; | |
151 | 33 | int64_t integer_part = ns; | |
152 | 33 | int i = 0; | |
153 |
4/4✓ Branch 0 taken 40 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 1 times.
|
72 | while (integer_part > 999 && i<3) { |
154 | 39 | decimal_part = integer_part % 1000; | |
155 | 39 | integer_part = integer_part / 1000; | |
156 | 39 | ++i; | |
157 | } | ||
158 | |||
159 | /* Print 0 or 2 decimals */ | ||
160 | 33 | decimal_part /= 10; | |
161 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 18 times.
|
33 | if (decimal_part > 0) { |
162 | 15 | snprintf(buf, size, "%"PRId64".%02d %s", integer_part, decimal_part, units[i]); | |
163 | } else { | ||
164 | 18 | snprintf(buf, size, "%"PRId64" %s", integer_part, units[i]); | |
165 | } | ||
166 | } | ||
167 | |||
168 | |||
169 | /* Timers */ | ||
170 | |||
171 | enum { TIMER_MAX_KEY_LEN = 128 }; | ||
172 | |||
173 | typedef struct TimerData { | ||
174 | char key[TIMER_MAX_KEY_LEN]; | ||
175 | int64_t acc; | ||
176 | int64_t asq; | ||
177 | int64_t max; | ||
178 | int64_t count; | ||
179 | struct timespec start; | ||
180 | struct timespec stop; | ||
181 | } timer_data_t; | ||
182 | |||
183 | static timer_data_t *timers = NULL; | ||
184 | static int ntimers = 0; | ||
185 | |||
186 | 81 | void timer_init(void) { | |
187 | 81 | } | |
188 | |||
189 | ✗ | void *timer_register(const char *key) { | |
190 | /* Found key if already registered */ | ||
191 | int i; | ||
192 | ✗ | for (i=0; i<ntimers; ++i) { | |
193 | ✗ | if (strncmp(timers[i].key, key, TIMER_MAX_KEY_LEN) == 0) { | |
194 | ✗ | return &timers[i]; | |
195 | } | ||
196 | } | ||
197 | |||
198 | /* Reallocate new position in timers array */ | ||
199 | ✗ | ++ntimers; | |
200 | ✗ | void *p = realloc(timers, sizeof(timer_data_t)*ntimers); | |
201 | ✗ | if (p) timers = p; | |
202 | ✗ | else fatal("realloc failed"); | |
203 | |||
204 | /* Initialize timer */ | ||
205 | ✗ | timer_data_t *timer = &timers[ntimers-1]; | |
206 | ✗ | snprintf(&timer->key[0], TIMER_MAX_KEY_LEN, "%s", key); | |
207 | ✗ | timer->acc = 0; | |
208 | ✗ | timer->asq = 0; | |
209 | ✗ | timer->max = 0; | |
210 | ✗ | timer->count = 0; | |
211 | ✗ | reset(&timer->start); | |
212 | ✗ | reset(&timer->stop); | |
213 | |||
214 | ✗ | return timer; | |
215 | } | ||
216 | |||
217 | ✗ | void timer_start(void *handler) { | |
218 | ✗ | timer_data_t *timer = (timer_data_t*) handler; | |
219 | ✗ | timer->count++; | |
220 | ✗ | get_time(&timer->start); | |
221 | } | ||
222 | |||
223 | ✗ | void timer_stop(void *handler) { | |
224 | ✗ | timer_data_t *timer = (timer_data_t*) handler; | |
225 | ✗ | get_time(&timer->stop); | |
226 | ✗ | int64_t elapsed = timespec_diff(&timer->start, &timer->stop); | |
227 | ✗ | timer->acc += elapsed; | |
228 | ✗ | timer->asq += elapsed*elapsed; | |
229 | ✗ | timer->max = elapsed > timer->max ? elapsed : timer->max; | |
230 | } | ||
231 | |||
232 | 79 | void timer_finalize(void) { | |
233 | /* Timers Report */ | ||
234 | int i; | ||
235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
|
79 | for (i=0; i<ntimers; ++i) { |
236 | ✗ | timer_data_t *timer = &timers[i]; | |
237 | ✗ | int64_t avg = timer->acc/timer->count; | |
238 | ✗ | int64_t stdev = sqrt(timer->asq/timer->count - avg*avg); | |
239 | ✗ | info("Timer \"%s\" " | |
240 | "n: %"PRId64", avg: %"PRId64", stdev: %"PRId64", max: %"PRId64, | ||
241 | ✗ | timer->key, timer->count, avg, stdev, timer->max); | |
242 | } | ||
243 | |||
244 | /* De-allocate timers */ | ||
245 | 79 | free(timers); | |
246 | 79 | timers = NULL; | |
247 | 79 | ntimers = 0; | |
248 | 79 | } | |
249 | |||
250 | |||
251 | /* Formatted strings */ | ||
252 | |||
253 | // This function assumes localtime to be used, so no timezone information is specified | ||
254 | 21 | char* get_iso_8601_string(struct tm *tm_info) { | |
255 | // requires 20 characters plus the null terminator | ||
256 | 21 | char *iso_8601_string = malloc(20); | |
257 | |||
258 | // Format the tm structure into the ISO 8601 string | ||
259 | 21 | strftime(iso_8601_string, 20, "%Y-%m-%dT%H:%M:%S", tm_info); | |
260 | |||
261 | 21 | return iso_8601_string; | |
262 | } | ||
263 |