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