| 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 | 13298 | void get_time( struct timespec *t ) { | |
| 33 | 13298 | clock_gettime( CLOCK_MONOTONIC, t); | |
| 34 | 13298 | } | |
| 35 | |||
| 36 | 103 | void get_time_coarse( struct timespec *t ) { | |
| 37 | #ifdef CLOCK_MONOTONIC_COARSE | ||
| 38 | 103 | clock_gettime( CLOCK_MONOTONIC_COARSE, t); | |
| 39 | #else | ||
| 40 | clock_gettime( CLOCK_MONOTONIC, t); | ||
| 41 | #endif | ||
| 42 | 103 | } | |
| 43 | |||
| 44 | 529 | 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 | 529 | clock_gettime(CLOCK_REALTIME, t); | |
| 48 | 529 | } | |
| 49 | |||
| 50 | 13078 | int64_t get_time_in_ns(void) { | |
| 51 | struct timespec t; | ||
| 52 | 13078 | get_time(&t); | |
| 53 | 13078 | 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 | 13084 | int64_t to_nsecs( const struct timespec *ts ) { | |
| 115 | 13084 | return ts->tv_nsec + (int64_t)ts->tv_sec * NS_PER_SECOND; | |
| 116 | } | ||
| 117 | |||
| 118 | 38 | double nsecs_to_secs( int64_t nsecs ) { | |
| 119 | 38 | 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 | 99 | int64_t timespec_diff( const struct timespec *init, const struct timespec *end ) { | |
| 130 | 198 | return (int64_t)(end->tv_sec - init->tv_sec) * NS_PER_SECOND + | |
| 131 | 99 | (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 | 42 | void ns_to_human( char *buf, size_t size, int64_t ns ) { | |
| 147 |
4/4✓ Branch 0 taken 41 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 40 times.
|
42 | if (!buf || !size) return; |
| 148 | |||
| 149 | 40 | const char* const units[] = {"ns", "us", "ms", "s"}; | |
| 150 | 40 | int decimal_part = 0; | |
| 151 | 40 | int64_t integer_part = ns; | |
| 152 | 40 | int i = 0; | |
| 153 |
4/4✓ Branch 0 taken 52 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 1 times.
|
91 | while (integer_part > 999 && i<3) { |
| 154 | 51 | decimal_part = integer_part % 1000; | |
| 155 | 51 | integer_part = integer_part / 1000; | |
| 156 | 51 | ++i; | |
| 157 | } | ||
| 158 | |||
| 159 | /* Print 0 or 2 decimals */ | ||
| 160 | 40 | decimal_part /= 10; | |
| 161 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 14 times.
|
40 | if (decimal_part > 0) { |
| 162 | 26 | snprintf(buf, size, "%"PRId64".%02d %s", integer_part, decimal_part, units[i]); | |
| 163 | } else { | ||
| 164 | 14 | 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 | 92 | void timer_init(void) { | |
| 187 | 92 | } | |
| 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 | 90 | void timer_finalize(void) { | |
| 233 | /* Timers Report */ | ||
| 234 | int i; | ||
| 235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
|
90 | 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 | 90 | free(timers); | |
| 246 | 90 | timers = NULL; | |
| 247 | 90 | ntimers = 0; | |
| 248 | 90 | } | |
| 249 | |||
| 250 | |||
| 251 | /* Formatted strings */ | ||
| 252 | |||
| 253 | // This function assumes localtime to be used, so no timezone information is specified | ||
| 254 | 29 | char* get_iso_8601_string(struct tm *tm_info) { | |
| 255 | // requires 20 characters plus the null terminator | ||
| 256 | 29 | char *iso_8601_string = malloc(20); | |
| 257 | |||
| 258 | // Format the tm structure into the ISO 8601 string | ||
| 259 | 29 | strftime(iso_8601_string, 20, "%Y-%m-%dT%H:%M:%S", tm_info); | |
| 260 | |||
| 261 | 29 | return iso_8601_string; | |
| 262 | } | ||
| 263 |