1 /*============================================================================
2 * Program timing information
3 *============================================================================*/
4
5 /*
6 This file is part of Code_Saturne, a general-purpose CFD tool.
7
8 Copyright (C) 1998-2021 EDF S.A.
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
13 version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22 Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 /*----------------------------------------------------------------------------*/
26
27 #if defined(HAVE_CONFIG_H)
28 # include "cs_config.h"
29 #endif
30
31 #if defined(HAVE_CLOCK_GETTIME)
32 #if !defined(_POSIX_C_SOURCE)
33 #endif
34 #endif
35
36 /*-----------------------------------------------------------------------------*/
37
38 #include "cs_defs.h"
39
40 /*----------------------------------------------------------------------------
41 * Standard C library headers
42 *----------------------------------------------------------------------------*/
43
44 #include <math.h>
45 #include <time.h>
46
47 #if defined (HAVE_GETTIMEOFDAY)
48 #include <sys/time.h>
49 #endif
50
51 #if defined (HAVE_GETRUSAGE)
52 #include <sys/time.h>
53 #include <sys/resource.h>
54 #include <unistd.h>
55 #endif
56
57 #if defined(_POSIX_SOURCE)
58 #include <sys/times.h>
59 #include <unistd.h>
60 #endif
61
62 /* Disable automatically-defined HAVE_CLOCK_GETTIME on Cygwin */
63
64 #if defined(HAVE_CLOCK_GETTIME) && defined(__CYGWIN__)
65 #undef HAVE_CLOCK_GETTIME
66 #endif
67
68 /*----------------------------------------------------------------------------
69 * Local headers
70 *----------------------------------------------------------------------------*/
71
72 /*----------------------------------------------------------------------------
73 * Header for the current file
74 *----------------------------------------------------------------------------*/
75
76 #include "cs_timer.h"
77
78 /*----------------------------------------------------------------------------*/
79
80 BEGIN_C_DECLS
81
82 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
83
84 /*-----------------------------------------------------------------------------
85 * Local type definitions
86 *-----------------------------------------------------------------------------*/
87
88 /* Timing methods */
89
90 typedef enum {
91
92 CS_TIMER_DISABLE,
93 CS_TIMER_CLOCK_GETTIME,
94 CS_TIMER_GETTIMEOFDAY,
95 CS_TIMER_GETRUSAGE,
96 CS_TIMER_TIME,
97 CS_TIMER_TIMES,
98 CS_TIMER_CLOCK
99
100 } cs_timer_method_t;
101
102 /* Function pointer types */
103
104 typedef void
105 (_cs_timer_wall_func_t) (cs_timer_t *timer);
106
107 typedef void
108 (_cs_timer_cpu_func_t) (cs_timer_t *timer);
109
110 /*-------------------------------------------------------------------------------
111 * Local macro documentation
112 *-----------------------------------------------------------------------------*/
113
114 /*! \fn CS_TIMER_COUNTER_INIT(_t)
115 *
116 * \brief Initialize timer counter.
117 *
118 * \param [out] _t resulting counter.
119 */
120
121 /*! \fn CS_TIMER_COUNTER_ADD(_res, _c0, _c1)
122 *
123 * \brief Add timer counter.
124 *
125 * The result may be identical to one of the 2 counters to add.
126 *
127 * \param [out] _res resulting counter.
128 * \param [in] _c0 counter to add.
129 * \param [in] _c1 counter to add.
130 */
131
132 /*-----------------------------------------------------------------------------
133 * Local function prototypes (descriptions later).
134 *-----------------------------------------------------------------------------*/
135
136 /* Wall-clock timer functions */
137
138 void
139 _cs_timer_wall_null(cs_timer_t *timer);
140
141 #if defined (HAVE_CLOCK_GETTIME)
142 void
143 _cs_timer_wall_clock_gettime(cs_timer_t *timer);
144 #endif
145
146 #if defined (HAVE_GETTIMEOFDAY)
147 void
148 _cs_timer_wall_gettimeofday(cs_timer_t *timer);
149 #endif
150
151 void
152 _cs_timer_wall_stdc_time(cs_timer_t *timer);
153
154 /* CPU timer functions */
155
156 void
157 _cs_timer_cpu_null(cs_timer_t *timer);
158
159 #if defined (HAVE_CLOCK_GETTIME)
160 void
161 _cs_timer_cpu_clock_gettime(cs_timer_t *timer);
162 #endif
163
164 #if defined (HAVE_GETRUSAGE)
165 void
166 _cs_timer_cpu_getrusage(cs_timer_t *timer);
167 #endif
168
169 #if defined(_POSIX_SOURCE)
170 void
171 _cs_timer_cpu_times(cs_timer_t *timer);
172 #endif
173
174 void
175 _cs_timer_cpu_stdc_clock(cs_timer_t *timer);
176
177 /*-----------------------------------------------------------------------------
178 * Local static variable definitions
179 *-----------------------------------------------------------------------------*/
180
181 static bool _cs_timer_initialized = false;
182 static cs_timer_method_t _cs_timer_wall_method = CS_TIMER_DISABLE;
183 static cs_timer_method_t _cs_timer_cpu_method = CS_TIMER_DISABLE;
184 static _cs_timer_wall_func_t *_cs_timer_wall = _cs_timer_wall_null;
185 static _cs_timer_cpu_func_t *_cs_timer_cpu = _cs_timer_cpu_null;
186
187 /* Wall-clock time */
188
189 static time_t _cs_timer_stdc_time_start;
190
191 /* CPU time */
192
193 #if defined(_POSIX_SOURCE)
194 static long _cs_timer_unit = 0;
195 #endif
196
197 static clock_t _cs_timer_clock_start;
198
199 /* Reference times */
200
201 static cs_timer_t _cs_timer_start = {.sec = 0, .nsec = 0};
202 static cs_timer_t _cs_timer_cpu_start = {.sec = 0, .nsec = 0};
203
204 /*============================================================================
205 * Private function definitions
206 *============================================================================*/
207
208 /*----------------------------------------------------------------------------
209 * Null function to get wall-clock time.
210 *
211 * parameters:
212 * timer <-- pointer to timing structure (ignored)
213 *----------------------------------------------------------------------------*/
214
215 void
_cs_timer_wall_null(cs_timer_t * timer)216 _cs_timer_wall_null(cs_timer_t *timer)
217 {
218 CS_UNUSED(timer);
219 }
220
221 #if defined (HAVE_CLOCK_GETTIME)
222
223 /*----------------------------------------------------------------------------
224 * Get wall-clock time using clock_gettime().
225 *
226 * parameters:
227 * timer <-- pointer to timing structure
228 *----------------------------------------------------------------------------*/
229
230 void
_cs_timer_wall_clock_gettime(cs_timer_t * timer)231 _cs_timer_wall_clock_gettime(cs_timer_t *timer)
232 {
233 struct timespec w_time;
234 (void)clock_gettime(CLOCK_REALTIME, &w_time);
235 timer->sec = w_time.tv_sec;
236 timer->nsec = w_time.tv_nsec;
237 }
238
239 #endif /* defined (HAVE_CLOCK_GETTIME) */
240
241 #if defined (HAVE_GETTIMEOFDAY)
242
243 /*----------------------------------------------------------------------------
244 * Get wall-clock time using gettimeofday().
245 *
246 * parameters:
247 * timer <-- pointer to timing structure
248 *----------------------------------------------------------------------------*/
249
250 void
_cs_timer_wall_gettimeofday(cs_timer_t * timer)251 _cs_timer_wall_gettimeofday(cs_timer_t *timer)
252 {
253 struct timeval tv_time;
254 (void)gettimeofday(&tv_time, NULL);
255 timer->sec = tv_time.tv_sec;
256 timer->nsec = tv_time.tv_usec*1000;
257 }
258
259 #endif /* defined (HAVE_GETTIMEOFDAY) */
260
261 /*----------------------------------------------------------------------------
262 * Get wall-clock time using time().
263 *
264 * parameters:
265 * timer <-- pointer to timing structure
266 *----------------------------------------------------------------------------*/
267
268 void
_cs_timer_wall_stdc_time(cs_timer_t * timer)269 _cs_timer_wall_stdc_time(cs_timer_t *timer)
270 {
271 time_t wtime_current;
272 double dt;
273 time(&wtime_current);
274 dt = difftime(wtime_current, _cs_timer_stdc_time_start);
275 timer->sec = floor(dt);
276 timer->nsec = (dt - timer->sec) * 1.0e-9;
277 }
278
279 /*----------------------------------------------------------------------------
280 * Null function to get CPU time.
281 *
282 * parameters:
283 * timer <-- pointer to timing structure (ignored)
284 *----------------------------------------------------------------------------*/
285
286 void
_cs_timer_cpu_null(cs_timer_t * timer)287 _cs_timer_cpu_null(cs_timer_t *timer)
288 {
289 CS_UNUSED(timer);
290 }
291
292 #if defined (HAVE_CLOCK_GETTIME)
293
294 /*----------------------------------------------------------------------------
295 * Get CPU time using clock_gettime().
296 *
297 * parameters:
298 * timer <-- pointer to timing structure
299 *----------------------------------------------------------------------------*/
300
301 void
_cs_timer_cpu_clock_gettime(cs_timer_t * timer)302 _cs_timer_cpu_clock_gettime(cs_timer_t *timer)
303 {
304 struct timespec cpu_time;
305 (void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_time);
306 timer->sec = cpu_time.tv_sec;
307 timer->nsec = cpu_time.tv_nsec;
308 }
309
310 #endif /* defined (HAVE_CLOCK_GETTIME) */
311
312 #if defined (HAVE_GETRUSAGE)
313
314 /*----------------------------------------------------------------------------
315 * Get CPU time using clock_getrusage().
316 *
317 * parameters:
318 * timer <-- pointer to timing structure
319 *----------------------------------------------------------------------------*/
320
321 void
_cs_timer_cpu_getrusage(cs_timer_t * timer)322 _cs_timer_cpu_getrusage(cs_timer_t *timer)
323 {
324 struct rusage usage;
325 getrusage(RUSAGE_SELF, &usage);
326 timer->sec = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
327 timer->nsec = (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec)*1000;
328 }
329
330 #endif /* defined (HAVE_GETRUSAGE) */
331
332 #if defined(_POSIX_SOURCE)
333
334 /*----------------------------------------------------------------------------
335 * Get CPU time using times().
336 *
337 * parameters:
338 * timer <-- pointer to timing structure
339 *----------------------------------------------------------------------------*/
340
341 void
_cs_timer_cpu_times(cs_timer_t * timer)342 _cs_timer_cpu_times(cs_timer_t *timer)
343 {
344 struct tms ptimer;
345 clock_t ticks;
346 times(&ptimer);
347 ticks = ptimer.tms_utime + ptimer.tms_stime;
348 timer->cpu_sec = ticks / _cs_timer_unit;
349 timer->cpu_nsec = (double)(ticks % _cs_timer_unit)*1.e+9 / _cs_timer_unit;
350 }
351
352 #endif /* defined(_POSIX_SOURCE) */
353
354 /*----------------------------------------------------------------------------
355 * Get CPU time using clock().
356 *
357 * parameters:
358 * timer <-- pointer to timing structure
359 *----------------------------------------------------------------------------*/
360
361 void
_cs_timer_cpu_stdc_clock(cs_timer_t * timer)362 _cs_timer_cpu_stdc_clock(cs_timer_t *timer)
363 {
364 clock_t clock_current = clock() - _cs_timer_clock_start;
365 timer->sec = clock_current / CLOCKS_PER_SEC;
366 timer->nsec = (clock_current % CLOCKS_PER_SEC)*1.e+9/CLOCKS_PER_SEC;
367 }
368
369 /*----------------------------------------------------------------------------
370 * Initialize timers.
371 *----------------------------------------------------------------------------*/
372
373 static void
_cs_timer_initialize(void)374 _cs_timer_initialize(void)
375 {
376 _cs_timer_start.sec = 0;
377 _cs_timer_start.nsec = 0;
378
379 _cs_timer_cpu_start.sec = 0;
380 _cs_timer_cpu_start.nsec = 0;
381
382 /* Select timing methods, trying highest resolution first */
383
384 #if defined (HAVE_CLOCK_GETTIME)
385
386 struct timespec ts_time;
387
388 if (_cs_timer_wall_method == CS_TIMER_DISABLE) {
389 if (clock_gettime(CLOCK_REALTIME, &ts_time) == 0) {
390 _cs_timer_start.sec = ts_time.tv_sec;
391 _cs_timer_start.nsec = ts_time.tv_nsec;
392 _cs_timer_wall_method = CS_TIMER_CLOCK_GETTIME;
393 _cs_timer_wall = _cs_timer_wall_clock_gettime;
394 }
395 }
396
397 #if defined (HAVE_CLOCK_GETCPUCLOCKID)
398 if (_cs_timer_cpu_method == CS_TIMER_DISABLE) {
399 clockid_t clock_id;
400 if (clock_getcpuclockid(0, &clock_id) == 0) {
401 /* ENOENT could be allowed with process binding,
402 but binding needs to be checked. */
403 if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts_time) == 0) {
404 _cs_timer_cpu_method = CS_TIMER_CLOCK_GETTIME;
405 _cs_timer_cpu = _cs_timer_cpu_clock_gettime;
406 }
407 }
408 }
409 #endif
410
411 #endif
412
413 #if defined (HAVE_GETTIMEOFDAY)
414
415 if (_cs_timer_wall_method == CS_TIMER_DISABLE) {
416 static struct timeval tv_time;
417 if (gettimeofday(&tv_time, NULL) == 0) {
418 _cs_timer_start.sec = tv_time.tv_sec;
419 _cs_timer_start.nsec = tv_time.tv_usec*1000;
420 _cs_timer_wall_method = CS_TIMER_GETTIMEOFDAY;
421 _cs_timer_wall = _cs_timer_wall_gettimeofday;
422 }
423 }
424
425 #endif
426
427 #if defined(HAVE_GETRUSAGE)
428
429 if (_cs_timer_cpu_method == CS_TIMER_DISABLE) {
430 struct rusage usage;
431 if (getrusage(RUSAGE_SELF, &usage) == 0) {
432 _cs_timer_cpu_method = CS_TIMER_GETRUSAGE;
433 _cs_timer_cpu = _cs_timer_cpu_getrusage;
434 }
435 }
436
437 #endif
438
439 #if defined(_POSIX_SOURCE)
440
441 if (_cs_timer_cpu_method == CS_TIMER_DISABLE) {
442 static struct tms ptimer;
443 _cs_timer_unit = sysconf(_SC_CLK_TCK);
444 if (_cs_timer_unit != -1 && times(&ptimer) != -1) {
445 _cs_timer_cpu_method = CS_TIMER_TIMES;
446 _cs_timer_cpu = _cs_timer_cpu_times;
447 }
448 }
449
450 #endif /* defined(_POSIX_SOURCE) */
451
452 /* Use minimal C library functions */
453
454 if (_cs_timer_wall_method == CS_TIMER_DISABLE) {
455 time_t wtime_current;
456 if (time(&wtime_current) != (time_t)-1) {
457 _cs_timer_stdc_time_start = time(&wtime_current);
458 _cs_timer_wall_method = CS_TIMER_TIME;
459 _cs_timer_wall = _cs_timer_wall_stdc_time;
460 }
461 }
462
463 if (_cs_timer_cpu_method == CS_TIMER_DISABLE) {
464 _cs_timer_clock_start = clock();
465 if (_cs_timer_clock_start != (clock_t)-1) {
466 _cs_timer_cpu_method = CS_TIMER_CLOCK;
467 _cs_timer_cpu = _cs_timer_cpu_stdc_clock;
468 }
469 }
470
471 _cs_timer_initialized = true;
472 }
473
474 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
475
476 /*============================================================================
477 * Public function definitions
478 *============================================================================*/
479
480 /*----------------------------------------------------------------------------*/
481 /*!
482 * \brief Return Wall clock time
483 *
484 * \return elapsed time from first call of a function of the cs_timer_...()
485 * series, or -1 if unable to compute.
486 */
487 /*----------------------------------------------------------------------------*/
488
489 double
cs_timer_wtime(void)490 cs_timer_wtime(void)
491 {
492 cs_timer_t t1;
493
494 /* Ensure initialization */
495
496 if (_cs_timer_initialized == false)
497 _cs_timer_initialize();
498
499 /* Compute elapsed time */
500
501 _cs_timer_wall(&t1);
502
503 long long wall_nsec
504 = (t1.sec - _cs_timer_start.sec) * (long long)1000000000
505 + t1.nsec - _cs_timer_start.nsec;
506
507 return wall_nsec*1.e-9;
508 }
509
510 /*----------------------------------------------------------------------------*/
511 /*!
512 * \brief Return CPU time.
513 *
514 * Note that in the rare case that only the minimal C library clock()
515 * method is available (see cs_timer_cpu_time_method()), at least one of
516 * the cs_timer_...() functions (possibly this one) must be called
517 * upon program start for this function to be used. In addition,
518 * in this case, time may "loop" back to 0 every multiple of
519 * 2^size_t / CLOCKS_PER_SEC seconds.
520 *
521 * \return current CPU time usage, or -1 if unable to compute.
522 */
523 /*----------------------------------------------------------------------------*/
524
525 double
cs_timer_cpu_time(void)526 cs_timer_cpu_time(void)
527 {
528 cs_timer_t t1;
529
530 /* Ensure initialization */
531
532 if (_cs_timer_initialized == 0)
533 _cs_timer_initialize();
534
535 /* Compute CPU time */
536
537 _cs_timer_cpu(&t1);
538
539 long long cpu_nsec
540 = (t1.sec - _cs_timer_cpu_start.sec) * (long long)1000000000
541 + t1.nsec - _cs_timer_cpu_start.nsec;
542
543 return cpu_nsec*1.e-9;
544 }
545
546 /*----------------------------------------------------------------------------*/
547 /*!
548 * \brief Return separate user and system CPU times.
549 *
550 * Note that in the rare case that only the minimal C library clock()
551 * method is available, this function will return -1 values.
552 *
553 * \param [out] user_time current user CPU usage.
554 * \param [out] system_time current system CPU usage.
555 */
556 /*----------------------------------------------------------------------------*/
557
558 void
cs_timer_cpu_times(double * user_time,double * system_time)559 cs_timer_cpu_times(double *user_time,
560 double *system_time)
561
562 {
563 /* Ensure initialization */
564
565 if (_cs_timer_initialized == 0)
566 _cs_timer_initialize();
567
568 *user_time = -1.;
569 *system_time = -1.;
570
571 /* Compute CPU time */
572
573 #if defined (HAVE_GETRUSAGE)
574
575 {
576 struct rusage usage;
577
578 if (getrusage(RUSAGE_SELF, &usage) == 0) {
579 *user_time = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec * 1.e-6;
580 *system_time = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec * 1.e-6;
581 }
582 }
583
584 #elif defined(_POSIX_SOURCE)
585
586 {
587 static struct tms ptimer;
588
589 if (_cs_timer_unit != -1 && times(&ptimer) != -1) {
590 *user_time = ((double)ptimer.tms_utime) / _cs_timer_unit;
591 *system_time = ((double)ptimer.tms_stime) / _cs_timer_unit;
592 }
593
594 }
595
596 #endif
597 }
598
599 /*----------------------------------------------------------------------------*/
600 /*!
601 * \brief Return a timer's value
602 *
603 * \return timer structure.
604 */
605 /*----------------------------------------------------------------------------*/
606
607 cs_timer_t
cs_timer_time(void)608 cs_timer_time(void)
609 {
610 cs_timer_t time_current;
611
612 /* Ensure initialization */
613
614 if (_cs_timer_initialized == false)
615 _cs_timer_initialize();
616
617 /* Compute elapsed time */
618
619 _cs_timer_wall(&time_current);
620
621 return time_current;
622 }
623
624 /*----------------------------------------------------------------------------*/
625 /*!
626 * \brief Compute the difference between 2 timers.
627 *
628 * \param[in] t0 oldest timer value
629 * \param[in] t1 most recent timer value
630 *
631 * \return last - first timer value.
632 */
633 /*----------------------------------------------------------------------------*/
634
635 cs_timer_counter_t
cs_timer_diff(const cs_timer_t * t0,const cs_timer_t * t1)636 cs_timer_diff(const cs_timer_t *t0,
637 const cs_timer_t *t1)
638 {
639 cs_timer_counter_t retval;
640
641 retval.nsec = (t1->sec - t0->sec) * (long long)1000000000
642 + t1->nsec - t0->nsec;
643
644 return retval;
645 }
646
647 /*----------------------------------------------------------------------------*/
648 /*!
649 * \brief Return method used to return wall clock time.
650 *
651 * \return short description of method used to return wall clock time.
652 */
653 /*----------------------------------------------------------------------------*/
654
655 const char *
cs_timer_wtime_method(void)656 cs_timer_wtime_method(void)
657 {
658 if (_cs_timer_initialized == 0)
659 _cs_timer_initialize();
660
661 switch(_cs_timer_wall_method) {
662
663 case CS_TIMER_CLOCK_GETTIME:
664 return _("clock_gettime() function");
665 case CS_TIMER_GETTIMEOFDAY:
666 return _("gettimeofday() function");
667 case CS_TIMER_TIME:
668 return _("Iso C time() function");
669 default:
670 return _("Disabled");
671
672 }
673 }
674
675 /*----------------------------------------------------------------------------*/
676 /*!
677 * \brief Return method used to return CPU time.
678 *
679 * \return short description of method used to return CPU time.
680 */
681 /*----------------------------------------------------------------------------*/
682
683 const char *
cs_timer_cpu_time_method(void)684 cs_timer_cpu_time_method(void)
685 {
686 if (_cs_timer_initialized == 0)
687 _cs_timer_initialize();
688
689 switch(_cs_timer_wall_method) {
690
691 case CS_TIMER_CLOCK_GETTIME:
692 return _("clock_gettime() function");
693 case CS_TIMER_GETRUSAGE:
694 return _("getrusage() function");
695 case CS_TIMER_TIMES:
696 return _("Posix times() function");
697 case CS_TIMER_CLOCK:
698 return _("Iso C clock() function");
699 case CS_TIMER_DISABLE:
700 return _("Disabled");
701 default:
702 return _("Disabled");
703
704 }
705 }
706
707 /*-----------------------------------------------------------------------------*/
708
709 END_C_DECLS
710