Coverage Report

Created: 2026-03-04 00:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5timer.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 * H5timer.c
15
 *
16
 * Internal, platform-independent 'timer' support routines
17
 *-------------------------------------------------------------------------
18
 */
19
20
#include "H5module.h" /* This source code file is part of the H5 module */
21
22
#include "H5private.h"
23
24
/* Size of a generated time string.
25
 * Most time strings should be < 20 or so characters (max!) so this should be a
26
 * safe size.  Dynamically allocating the correct size would be painful.
27
 */
28
0
#define H5TIMER_TIME_STRING_LEN 1536
29
30
/* Conversion factors */
31
0
#define H5_SEC_PER_DAY  (24.0 * 60.0 * 60.0)
32
0
#define H5_SEC_PER_HOUR (60.0 * 60.0)
33
0
#define H5_SEC_PER_MIN  (60.0)
34
35
/*-------------------------------------------------------------------------
36
 * Function:  H5_bandwidth
37
 *
38
 * Purpose: Prints the bandwidth (bytes per second) in a field 10
39
 *    characters wide widh four digits of precision like this:
40
 *
41
 *             NaN  If <=0 seconds
42
 *      1234. TB/s
43
 *      123.4 TB/s
44
 *      12.34 GB/s
45
 *      1.234 MB/s
46
 *      4.000 kB/s
47
 *      1.000  B/s
48
 *      0.000  B/s  If NBYTES==0
49
 *      1.2345e-10  For bandwidth less than 1
50
 *      6.7893e+94  For exceptionally large values
51
 *      6.678e+106  For really big values
52
 *
53
 * Return:  void
54
 *-------------------------------------------------------------------------
55
 */
56
void
57
H5_bandwidth(char *buf /*out*/, size_t bufsize, double nbytes, double nseconds)
58
0
{
59
0
    double bw;
60
61
0
    if (nseconds <= 0.0)
62
0
        strcpy(buf, "       NaN");
63
0
    else {
64
0
        bw = nbytes / nseconds;
65
0
        if (H5_DBL_ABS_EQUAL(bw, 0.0))
66
0
            strcpy(buf, "0.000  B/s");
67
0
        else if (bw < 1.0)
68
0
            snprintf(buf, bufsize, "%10.4e", bw);
69
0
        else if (bw < (double)H5_KB) {
70
0
            snprintf(buf, bufsize, "%05.4f", bw);
71
0
            strcpy(buf + 5, "  B/s");
72
0
        }
73
0
        else if (bw < (double)H5_MB) {
74
0
            snprintf(buf, bufsize, "%05.4f", bw / (double)H5_KB);
75
0
            strcpy(buf + 5, " kB/s");
76
0
        }
77
0
        else if (bw < (double)H5_GB) {
78
0
            snprintf(buf, bufsize, "%05.4f", bw / (double)H5_MB);
79
0
            strcpy(buf + 5, " MB/s");
80
0
        }
81
0
        else if (bw < (double)H5_TB) {
82
0
            snprintf(buf, bufsize, "%05.4f", bw / (double)H5_GB);
83
0
            strcpy(buf + 5, " GB/s");
84
0
        }
85
0
        else if (bw < (double)H5_PB) {
86
0
            snprintf(buf, bufsize, "%05.4f", bw / (double)H5_TB);
87
0
            strcpy(buf + 5, " TB/s");
88
0
        }
89
0
        else if (bw < (double)H5_EB) {
90
0
            snprintf(buf, bufsize, "%05.4f", bw / (double)H5_PB);
91
0
            strcpy(buf + 5, " PB/s");
92
0
        }
93
0
        else {
94
0
            snprintf(buf, bufsize, "%10.4e", bw);
95
0
            if (strlen(buf) > 10)
96
0
                snprintf(buf, bufsize, "%10.3e", bw);
97
0
        }
98
0
    }
99
0
} /* end H5_bandwidth() */
100
101
/*-------------------------------------------------------------------------
102
 * Function:  H5_now
103
 *
104
 * Purpose: Retrieves the current time, as seconds after the UNIX epoch.
105
 *
106
 * Return:  # of seconds from the epoch (can't fail)
107
 *-------------------------------------------------------------------------
108
 */
109
time_t
110
H5_now(void)
111
0
{
112
0
    time_t now; /* Current time */
113
114
0
#ifdef H5_HAVE_GETTIMEOFDAY
115
0
    {
116
0
        struct timeval now_tv;
117
118
0
        HDgettimeofday(&now_tv, NULL);
119
0
        now = now_tv.tv_sec;
120
0
    }
121
#else
122
    now = time(NULL);
123
#endif
124
125
0
    return now;
126
0
} /* end H5_now() */
127
128
/*-------------------------------------------------------------------------
129
 * Function:  H5_now_usec
130
 *
131
 * Purpose: Retrieves the current time, as microseconds after the UNIX epoch.
132
 *
133
 * Return:  # of microseconds from the epoch (can't fail)
134
 *-------------------------------------------------------------------------
135
 */
136
uint64_t
137
H5_now_usec(void)
138
0
{
139
0
    uint64_t now; /* Current time, in microseconds */
140
141
0
#if defined(H5_HAVE_CLOCK_GETTIME)
142
0
    {
143
0
        struct timespec ts;
144
145
0
        clock_gettime(CLOCK_MONOTONIC, &ts);
146
147
        /* Cast all values in this expression to uint64_t to ensure that all intermediate
148
         * calculations are done in 64 bit, to prevent overflow */
149
0
        now = ((uint64_t)ts.tv_sec * ((uint64_t)1000 * (uint64_t)1000)) +
150
0
              ((uint64_t)ts.tv_nsec / (uint64_t)1000);
151
0
    }
152
#elif defined(H5_HAVE_GETTIMEOFDAY)
153
    {
154
        struct timeval now_tv;
155
156
        HDgettimeofday(&now_tv, NULL);
157
158
        /* Cast all values in this expression to uint64_t to ensure that all intermediate
159
         * calculations are done in 64 bit, to prevent overflow */
160
        now = ((uint64_t)now_tv.tv_sec * ((uint64_t)1000 * (uint64_t)1000)) + (uint64_t)now_tv.tv_usec;
161
    }
162
#else
163
    /* Cast all values in this expression to uint64_t to ensure that all intermediate calculations
164
     * are done in 64 bit, to prevent overflow */
165
    now = ((uint64_t)time(NULL) * ((uint64_t)1000 * (uint64_t)1000));
166
#endif
167
168
0
    return now;
169
0
} /* end H5_now_usec() */
170
171
/*--------------------------------------------------------------------------
172
 * Function:    H5_get_time
173
 *
174
 * Purpose:     Get the current time, as the time of seconds after the UNIX epoch
175
 *
176
 * Return:      Success:    A non-negative time value
177
 *              Failure:    -1.0 (in theory, can't currently fail)
178
 *--------------------------------------------------------------------------
179
 */
180
double
181
H5_get_time(void)
182
0
{
183
0
    double ret_value = 0.0;
184
185
0
    FUNC_ENTER_NOAPI_NOINIT_NOERR
186
187
0
#if defined(H5_HAVE_CLOCK_GETTIME)
188
0
    {
189
0
        struct timespec ts;
190
191
0
        clock_gettime(CLOCK_MONOTONIC, &ts);
192
0
        ret_value = (double)ts.tv_sec + ((double)ts.tv_nsec / 1000000000.0);
193
0
    }
194
#elif defined(H5_HAVE_GETTIMEOFDAY)
195
    {
196
        struct timeval now_tv;
197
198
        HDgettimeofday(&now_tv, NULL);
199
        ret_value = (double)now_tv.tv_sec + ((double)now_tv.tv_usec / 1000000.0);
200
    }
201
#else
202
    ret_value = (double)time(NULL);
203
#endif
204
205
0
    FUNC_LEAVE_NOAPI(ret_value)
206
0
} /* end H5_get_time() */
207
208
/*-------------------------------------------------------------------------
209
 * Function:    H5__timer_get_timevals
210
 *
211
 * Purpose:     Internal platform-specific function to get time system,
212
 *              user and elapsed time values.
213
 *
214
 * Return:      Success:    0
215
 *              Failure:    -1
216
 *-------------------------------------------------------------------------
217
 */
218
static herr_t
219
H5__timer_get_timevals(H5_timevals_t *times /*in,out*/)
220
0
{
221
0
    assert(times);
222
223
    /* Windows call handles both system/user and elapsed times */
224
#ifdef H5_HAVE_WIN32_API
225
    if (H5_get_win32_times(times) < 0) {
226
        times->elapsed = -1.0;
227
        times->system  = -1.0;
228
        times->user    = -1.0;
229
230
        return -1;
231
    }
232
#else /* H5_HAVE_WIN32_API */
233
234
    /*************************
235
     * System and user times *
236
     *************************/
237
0
#if defined(H5_HAVE_GETRUSAGE)
238
0
    {
239
0
        struct rusage res;
240
241
0
        if (getrusage(RUSAGE_SELF, &res) < 0)
242
0
            return -1;
243
0
        times->system = (double)res.ru_stime.tv_sec + ((double)res.ru_stime.tv_usec / 1.0E6);
244
0
        times->user   = (double)res.ru_utime.tv_sec + ((double)res.ru_utime.tv_usec / 1.0E6);
245
0
    }
246
#else
247
    /* No suitable way to get system/user times */
248
    /* This is not an error condition, they just won't be available */
249
    times->system = -1.0;
250
    times->user   = -1.0;
251
#endif
252
253
    /****************
254
     * Elapsed time *
255
     ****************/
256
257
0
    times->elapsed = H5_get_time();
258
259
0
#endif /* H5_HAVE_WIN32_API */
260
261
0
    return 0;
262
0
} /* end H5__timer_get_timevals() */
263
264
/*-------------------------------------------------------------------------
265
 * Function:    H5_timer_init
266
 *
267
 * Purpose:     Initialize a platform-independent timer.
268
 *
269
 *              Timer usage is as follows:
270
 *
271
 *              1) Call H5_timer_init(), passing in a timer struct, to set
272
 *                 up the timer.
273
 *
274
 *              2) Wrap any code you'd like to time with calls to
275
 *                 H5_timer_start/stop().  For accurate timing, place these
276
 *                 calls as close to the code of interest as possible.  You
277
 *                 can call start/stop multiple times on the same timer -
278
 *                 when you do this, H5_timer_get_times() will return time
279
 *                 values for the current/last session and
280
 *                 H5_timer_get_total_times() will return the summed times
281
 *                 of all sessions (see #3 and #4, below).
282
 *
283
 *              3) Use H5_timer_get_times() to get the current system, user
284
 *                 and elapsed times from a running timer.  If called on a
285
 *                 stopped timer, this will return the time recorded at the
286
 *                 stop point.
287
 *
288
 *              4) Call H5_timer_get_total_times() to get the total system,
289
 *                 user and elapsed times recorded across multiple start/stop
290
 *                 sessions.  If called on a running timer, it will return the
291
 *                 time recorded up to that point.  On a stopped timer, it
292
 *                 will return the time recorded at the stop point.
293
 *
294
 *              NOTE: Obtaining a time point is not free!  Keep in mind that
295
 *                    the time functions make system calls and can have
296
 *                    non-trivial overhead.  If you call one of the get_time
297
 *                    functions on a running timer, that overhead will be
298
 *                    added to the reported times.
299
 *
300
 *              5) All times recorded will be in seconds.  These can be
301
 *                 converted into human-readable strings with the
302
 *                 H5_timer_get_time_string() function.
303
 *
304
 *              6) A timer can be reset using by calling H5_timer_init() on
305
 *                 it.  This will set its state to 'stopped' and reset all
306
 *                 accumulated times to zero.
307
 *
308
 *
309
 * Return:      Success:    0
310
 *              Failure:    -1
311
 *-------------------------------------------------------------------------
312
 */
313
herr_t
314
H5_timer_init(H5_timer_t *timer /*in,out*/)
315
0
{
316
0
    assert(timer);
317
318
    /* Initialize everything */
319
0
    memset(timer, 0, sizeof(H5_timer_t));
320
321
0
    return 0;
322
0
} /* end H5_timer_init() */
323
324
/*-------------------------------------------------------------------------
325
 * Function:    H5_timer_start
326
 *
327
 * Purpose:     Start tracking time in a platform-independent timer.
328
 *
329
 * Return:      Success:    0
330
 *              Failure:    -1
331
 *-------------------------------------------------------------------------
332
 */
333
herr_t
334
H5_timer_start(H5_timer_t *timer /*in,out*/)
335
0
{
336
0
    assert(timer);
337
338
    /* Start the timer
339
     * This sets the "initial" times to the system-defined start times.
340
     */
341
0
    if (H5__timer_get_timevals(&(timer->initial)) < 0)
342
0
        return -1;
343
344
0
    timer->is_running = true;
345
346
0
    return 0;
347
0
} /* end H5_timer_start() */
348
349
/*-------------------------------------------------------------------------
350
 * Function:    H5_timer_stop
351
 *
352
 * Purpose:     Stop tracking time in a platform-independent timer.
353
 *
354
 * Return:      Success:    0
355
 *              Failure:    -1
356
 *-------------------------------------------------------------------------
357
 */
358
herr_t
359
H5_timer_stop(H5_timer_t *timer /*in,out*/)
360
0
{
361
0
    assert(timer);
362
363
    /* Stop the timer */
364
0
    if (H5__timer_get_timevals(&(timer->final_interval)) < 0)
365
0
        return -1;
366
367
    /* The "final" times are stored as intervals (final - initial)
368
     * for more useful reporting to the user.
369
     */
370
0
    timer->final_interval.elapsed = timer->final_interval.elapsed - timer->initial.elapsed;
371
0
    timer->final_interval.system  = timer->final_interval.system - timer->initial.system;
372
0
    timer->final_interval.user    = timer->final_interval.user - timer->initial.user;
373
374
    /* Add the intervals to the elapsed time */
375
0
    timer->total.elapsed += timer->final_interval.elapsed;
376
0
    timer->total.system += timer->final_interval.system;
377
0
    timer->total.user += timer->final_interval.user;
378
379
0
    timer->is_running = false;
380
381
0
    return 0;
382
0
} /* end H5_timer_stop() */
383
384
/*-------------------------------------------------------------------------
385
 * Function:    H5_timer_get_times
386
 *
387
 * Purpose:     Get the system, user and elapsed times from a timer.  These
388
 *              are the times since the timer was last started and will be
389
 *              0.0 in a timer that has not been started since it was
390
 *              initialized.
391
 *
392
 *              This function can be called either before or after
393
 *              H5_timer_stop() has been called.  If it is called before the
394
 *              stop function, the timer will continue to run.
395
 *
396
 *              The system and user times will be -1.0 if those times cannot
397
 *              be computed on a particular platform.  The elapsed time will
398
 *              always be present.
399
 *
400
 * Return:      Success:    0
401
 *              Failure:    -1
402
 *-------------------------------------------------------------------------
403
 */
404
herr_t
405
H5_timer_get_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/)
406
0
{
407
0
    assert(times);
408
409
0
    if (timer.is_running) {
410
0
        H5_timevals_t now;
411
412
        /* Get the current times and report the current intervals without
413
         * stopping the timer.
414
         */
415
0
        if (H5__timer_get_timevals(&now) < 0)
416
0
            return -1;
417
418
0
        times->elapsed = now.elapsed - timer.initial.elapsed;
419
0
        times->system  = now.system - timer.initial.system;
420
0
        times->user    = now.user - timer.initial.user;
421
0
    }
422
0
    else {
423
0
        times->elapsed = timer.final_interval.elapsed;
424
0
        times->system  = timer.final_interval.system;
425
0
        times->user    = timer.final_interval.user;
426
0
    }
427
428
0
    return 0;
429
0
} /* end H5_timer_get_times() */
430
431
/*-------------------------------------------------------------------------
432
 * Function:    H5_timer_get_total_times
433
 *
434
 * Purpose:     Get the TOTAL system, user and elapsed times recorded by
435
 *              the timer since its initialization.  This is the sum of all
436
 *              times recorded while the timer was running.
437
 *
438
 *              These will be 0.0 in a timer that has not been started
439
 *              since it was initialized.  Calling H5_timer_init() on a
440
 *              timer will reset these values to 0.0.
441
 *
442
 *              This function can be called either before or after
443
 *              H5_timer_stop() has been called.  If it is called before the
444
 *              stop function, the timer will continue to run.
445
 *
446
 *              The system and user times will be -1.0 if those times cannot
447
 *              be computed on a particular platform.  The elapsed time will
448
 *              always be present.
449
 *
450
 * Return:      Success:    0
451
 *              Failure:    -1
452
 *-------------------------------------------------------------------------
453
 */
454
herr_t
455
H5_timer_get_total_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/)
456
0
{
457
0
    assert(times);
458
459
0
    if (timer.is_running) {
460
0
        H5_timevals_t now;
461
462
        /* Get the current times and report the current totals without
463
         * stopping the timer.
464
         */
465
0
        if (H5__timer_get_timevals(&now) < 0)
466
0
            return -1;
467
468
0
        times->elapsed = timer.total.elapsed + (now.elapsed - timer.initial.elapsed);
469
0
        times->system  = timer.total.system + (now.system - timer.initial.system);
470
0
        times->user    = timer.total.user + (now.user - timer.initial.user);
471
0
    }
472
0
    else {
473
0
        times->elapsed = timer.total.elapsed;
474
0
        times->system  = timer.total.system;
475
0
        times->user    = timer.total.user;
476
0
    }
477
478
0
    return 0;
479
0
} /* end H5_timer_get_total_times() */
480
481
/*-------------------------------------------------------------------------
482
 * Function:    H5_timer_get_time_string
483
 *
484
 * Purpose:     Converts a time (in seconds) into a human-readable string
485
 *              suitable for log messages.
486
 *
487
 * Return:      Success:  The time string.
488
 *
489
 *                        The general format of the time string is:
490
 *
491
 *                        "N/A"                 time < 0 (invalid time)
492
 *                        "%.f ns"              time < 1 microsecond
493
 *                        "%.1f us"             time < 1 millisecond
494
 *                        "%.1f ms"             time < 1 second
495
 *                        "%.2f s"              time < 1 minute
496
 *                        "%.f m %.f s"         time < 1 hour
497
 *                        "%.f h %.f m %.f s"   longer times
498
 *
499
 *              Failure:  NULL
500
 *-------------------------------------------------------------------------
501
 */
502
char *
503
H5_timer_get_time_string(double seconds)
504
0
{
505
0
    char *s; /* output string */
506
507
    /* Used when the time is greater than 59 seconds */
508
0
    double days          = 0.0;
509
0
    double hours         = 0.0;
510
0
    double minutes       = 0.0;
511
0
    double remainder_sec = 0.0;
512
513
    /* Extract larger time units from count of seconds */
514
0
    if (seconds > 60.0) {
515
        /* Set initial # of seconds */
516
0
        remainder_sec = seconds;
517
518
        /* Extract days */
519
0
        days = floor(remainder_sec / H5_SEC_PER_DAY);
520
0
        remainder_sec -= (days * H5_SEC_PER_DAY);
521
522
        /* Extract hours */
523
0
        hours = floor(remainder_sec / H5_SEC_PER_HOUR);
524
0
        remainder_sec -= (hours * H5_SEC_PER_HOUR);
525
526
        /* Extract minutes */
527
0
        minutes = floor(remainder_sec / H5_SEC_PER_MIN);
528
0
        remainder_sec -= (minutes * H5_SEC_PER_MIN);
529
530
        /* The # of seconds left is in remainder_sec */
531
0
    }
532
533
    /* Allocate */
534
0
    if (NULL == (s = (char *)calloc(H5TIMER_TIME_STRING_LEN, sizeof(char))))
535
0
        return NULL;
536
537
    /* Do we need a format string? Some people might like a certain
538
     * number of milliseconds or s before spilling to the next highest
539
     * time unit.  Perhaps this could be passed as an integer.
540
     * (name? round_up_size? ?)
541
     */
542
0
    if (seconds < 0.0)
543
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "N/A");
544
0
    else if (H5_DBL_ABS_EQUAL(0.0, seconds))
545
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "0.0 s");
546
0
    else if (seconds < 1.0E-6)
547
        /* t < 1 us, Print time in ns */
548
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.f ns", seconds * 1.0E9);
549
0
    else if (seconds < 1.0E-3)
550
        /* t < 1 ms, Print time in us */
551
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.1f us", seconds * 1.0E6);
552
0
    else if (seconds < 1.0)
553
        /* t < 1 s, Print time in ms */
554
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.1f ms", seconds * 1.0E3);
555
0
    else if (seconds < H5_SEC_PER_MIN)
556
        /* t < 1 m, Print time in s */
557
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.2f s", seconds);
558
0
    else if (seconds < H5_SEC_PER_HOUR)
559
        /* t < 1 h, Print time in m and s */
560
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.f m %.f s", minutes, remainder_sec);
561
0
    else if (seconds < H5_SEC_PER_DAY)
562
        /* t < 1 d, Print time in h, m and s */
563
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.f h %.f m %.f s", hours, minutes, remainder_sec);
564
0
    else
565
        /* Print time in d, h, m and s */
566
0
        snprintf(s, H5TIMER_TIME_STRING_LEN, "%.f d %.f h %.f m %.f s", days, hours, minutes, remainder_sec);
567
568
0
    return s;
569
0
} /* end H5_timer_get_time_string() */