1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file clock.c
17 * @ingroup OTHER_CFILES
18 * @brief methods for clocks and timing issues
19 * @author Tobias Achterberg
20 */
21
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23
24 #include <assert.h>
25 #if defined(_WIN32) || defined(_WIN64)
26 #include <windows.h>
27 #else
28 #include <sys/times.h>
29 #include <sys/time.h>
30 #include <unistd.h>
31 #endif
32 #include <time.h>
33
34 #include "scip/def.h"
35 #include "scip/pub_message.h"
36 #include "blockmemshell/memory.h"
37 #include "scip/set.h"
38 #include "scip/clock.h"
39
40 #include "scip/struct_clock.h"
41
42 /** converts CPU clock ticks into seconds */
43 static
cputime2sec(clock_t cputime)44 SCIP_Real cputime2sec(
45 clock_t cputime /**< clock ticks for CPU time */
46 )
47 {
48 clock_t clocks_per_second;
49
50 #if defined(_WIN32) || defined(_WIN64)
51 clocks_per_second = 100;
52 #else
53 #ifndef CLK_TCK
54 clocks_per_second = sysconf(_SC_CLK_TCK);
55 #else
56 clocks_per_second = CLK_TCK;
57 #endif
58 #endif
59
60 return (SCIP_Real)cputime / (SCIP_Real)clocks_per_second;
61 }
62
63 /*lint -esym(*,timeval)*/
64 /*lint -esym(*,gettimeofday)*/
65
66 /** converts wall clock time into seconds */
67 static
walltime2sec(long sec,long usec)68 SCIP_Real walltime2sec(
69 long sec, /**< seconds counter */
70 long usec /**< microseconds counter */
71 )
72 {
73 return (SCIP_Real)sec + 0.000001 * (SCIP_Real)usec;
74 }
75
76 /** converts seconds into CPU clock ticks */
77 static
sec2cputime(SCIP_Real sec,clock_t * cputime)78 void sec2cputime(
79 SCIP_Real sec, /**< seconds */
80 clock_t* cputime /**< pointer to store clock ticks for CPU time */
81 )
82 {
83 clock_t clocks_per_second;
84
85 assert(cputime != NULL);
86
87 #if defined(_WIN32) || defined(_WIN64)
88 clocks_per_second = 100;
89 #else
90 #ifndef CLK_TCK
91 clocks_per_second = sysconf(_SC_CLK_TCK);
92 #else
93 clocks_per_second = CLK_TCK;
94 #endif
95 #endif
96 *cputime = (clock_t)(sec * clocks_per_second);
97 }
98
99 /** converts wall clock time into seconds */
100 static
sec2walltime(SCIP_Real sec,long * wallsec,long * wallusec)101 void sec2walltime(
102 SCIP_Real sec, /**< seconds */
103 long* wallsec, /**< pointer to store seconds counter */
104 long* wallusec /**< pointer to store microseconds counter */
105 )
106 {
107 assert(wallsec != NULL);
108 assert(wallusec != NULL);
109
110 *wallsec = (long)sec;
111 *wallusec = (long)((sec - *wallsec) * 1000000.0);
112 }
113
114
115 /** sets the clock's type and converts the clock timer accordingly */
116 static
clockSetType(SCIP_CLOCK * clck,SCIP_CLOCKTYPE newtype)117 void clockSetType(
118 SCIP_CLOCK* clck, /**< clock timer */
119 SCIP_CLOCKTYPE newtype /**< new clock type */
120 )
121 {
122 assert(clck != NULL);
123 assert(newtype != SCIP_CLOCKTYPE_DEFAULT);
124
125 if( clck->clocktype != newtype )
126 {
127 if( clck->clocktype == SCIP_CLOCKTYPE_DEFAULT )
128 {
129 assert(clck->nruns == 0);
130 clck->clocktype = newtype;
131 SCIPclockReset(clck);
132 SCIPdebugMessage("switched clock type to %d\n", newtype);
133 }
134 else
135 {
136 SCIP_Real sec;
137
138 sec = SCIPclockGetTime(clck);
139 clck->clocktype = newtype;
140 SCIPclockSetTime(clck, sec);
141 SCIPdebugMessage("switched clock type to %d (%g seconds -> %g seconds)\n", newtype, sec, SCIPclockGetTime(clck));
142 }
143 }
144 }
145
146 /** if the clock uses the default clock type and the default changed, converts the clock timer to the new type */
147 static
clockUpdateDefaultType(SCIP_CLOCK * clck,SCIP_CLOCKTYPE defaultclocktype)148 void clockUpdateDefaultType(
149 SCIP_CLOCK* clck, /**< clock timer */
150 SCIP_CLOCKTYPE defaultclocktype /**< default type of clock to use */
151 )
152 {
153 assert(clck != NULL);
154 assert(defaultclocktype != SCIP_CLOCKTYPE_DEFAULT);
155
156 if( clck->usedefault && clck->clocktype != defaultclocktype )
157 clockSetType(clck, defaultclocktype);
158 }
159
160 /** creates a clock and initializes it */
SCIPclockCreate(SCIP_CLOCK ** clck,SCIP_CLOCKTYPE clocktype)161 SCIP_RETCODE SCIPclockCreate(
162 SCIP_CLOCK** clck, /**< pointer to clock timer */
163 SCIP_CLOCKTYPE clocktype /**< type of clock */
164 )
165 {
166 assert(clck != NULL);
167
168 SCIP_ALLOC( BMSallocMemory(clck) );
169
170 SCIPclockInit(*clck, clocktype);
171
172 return SCIP_OKAY;
173 }
174
175 /** frees a clock */
SCIPclockFree(SCIP_CLOCK ** clck)176 void SCIPclockFree(
177 SCIP_CLOCK** clck /**< pointer to clock timer */
178 )
179 {
180 assert(clck != NULL);
181
182 BMSfreeMemory(clck);
183 }
184
185 /** initializes and resets a clock */
SCIPclockInit(SCIP_CLOCK * clck,SCIP_CLOCKTYPE clocktype)186 void SCIPclockInit(
187 SCIP_CLOCK* clck, /**< clock timer */
188 SCIP_CLOCKTYPE clocktype /**< type of clock */
189 )
190 {
191 assert(clck != NULL);
192
193 SCIPdebugMessage("initializing clock %p of type %d\n", (void*)clck, clocktype);
194 clck->enabled = TRUE;
195 clck->lasttime = 0.0;
196 SCIPclockSetType(clck, clocktype);
197 }
198
199 /** completely stop the clock and reset the clock's counter to zero */
SCIPclockReset(SCIP_CLOCK * clck)200 void SCIPclockReset(
201 SCIP_CLOCK* clck /**< clock timer */
202 )
203 {
204 assert(clck != NULL);
205
206 SCIPdebugMessage("resetting clock %p of type %d (usedefault=%u)\n", (void*)clck, clck->clocktype, clck->usedefault);
207 switch( clck->clocktype )
208 {
209 case SCIP_CLOCKTYPE_DEFAULT:
210 break;
211 case SCIP_CLOCKTYPE_CPU:
212 clck->data.cpuclock.user = 0;
213 break;
214 case SCIP_CLOCKTYPE_WALL:
215 clck->data.wallclock.sec = 0;
216 clck->data.wallclock.usec = 0;
217 break;
218 default:
219 SCIPerrorMessage("invalid clock type\n");
220 SCIPABORT();
221 }
222 clck->nruns = 0;
223 }
224
225 /** enables the clock */
SCIPclockEnable(SCIP_CLOCK * clck)226 void SCIPclockEnable(
227 SCIP_CLOCK* clck /**< clock timer */
228 )
229 {
230 assert(clck != NULL);
231
232 SCIPdebugMessage("enabling clock %p of type %d (usedefault=%u)\n", (void*)clck, clck->clocktype, clck->usedefault);
233
234 clck->enabled = TRUE;
235 }
236
237 /** disables and resets the clock */
SCIPclockDisable(SCIP_CLOCK * clck)238 void SCIPclockDisable(
239 SCIP_CLOCK* clck /**< clock timer */
240 )
241 {
242 assert(clck != NULL);
243
244 SCIPdebugMessage("disabling clock %p of type %d (usedefault=%u)\n", (void*)clck, clck->clocktype, clck->usedefault);
245
246 clck->enabled = FALSE;
247 SCIPclockReset(clck);
248 }
249
250 /** enables or disables \p clck, depending on the value of the flag */
SCIPclockEnableOrDisable(SCIP_CLOCK * clck,SCIP_Bool enable)251 void SCIPclockEnableOrDisable(
252 SCIP_CLOCK* clck, /**< the clock to be disabled/enabled */
253 SCIP_Bool enable /**< should the clock be enabled? */
254 )
255 {
256 assert(clck != NULL);
257
258 if( enable )
259 SCIPclockEnable(clck);
260 else
261 SCIPclockDisable(clck);
262 }
263
264 /** sets the type of the clock, overriding the default clock type, and resets the clock */
SCIPclockSetType(SCIP_CLOCK * clck,SCIP_CLOCKTYPE clocktype)265 void SCIPclockSetType(
266 SCIP_CLOCK* clck, /**< clock timer */
267 SCIP_CLOCKTYPE clocktype /**< type of clock */
268 )
269 {
270 assert(clck != NULL);
271
272 SCIPdebugMessage("setting type of clock %p (type %d, usedefault=%u) to %d\n",
273 (void*)clck, clck->clocktype, clck->usedefault, clocktype);
274
275 clck->clocktype = clocktype;
276 clck->usedefault = (clocktype == SCIP_CLOCKTYPE_DEFAULT);
277 SCIPclockReset(clck);
278 }
279
280 /** starts measurement of time in the given clock */
SCIPclockStart(SCIP_CLOCK * clck,SCIP_SET * set)281 void SCIPclockStart(
282 SCIP_CLOCK* clck, /**< clock timer */
283 SCIP_SET* set /**< global SCIP settings */
284 )
285 {
286 assert(clck != NULL);
287 assert(set != NULL);
288
289 if( set->time_enabled && clck->enabled )
290 {
291 clockUpdateDefaultType(clck, set->time_clocktype);
292
293 if( clck->nruns == 0 )
294 {
295 #if defined(_WIN32) || defined(_WIN64)
296 FILETIME creationtime;
297 FILETIME exittime;
298 FILETIME kerneltime;
299 FILETIME usertime;
300 #else
301 struct timeval tp; /*lint !e86*/
302 struct tms now;
303 #endif
304
305 SCIPdebugMessage("starting clock %p (type %d, usedefault=%u)\n", (void*)clck, clck->clocktype, clck->usedefault);
306
307 switch( clck->clocktype )
308 {
309 case SCIP_CLOCKTYPE_CPU:
310 #if defined(_WIN32) || defined(_WIN64)
311 GetProcessTimes(GetCurrentProcess(), &creationtime, &exittime, &kerneltime, &usertime);
312 clck->data.cpuclock.user -= usertime.dwHighDateTime * 42950 + usertime.dwLowDateTime / 100000L;
313 #else
314 (void)times(&now);
315 clck->data.cpuclock.user -= now.tms_utime;
316 #endif
317 clck->lasttime = cputime2sec(clck->data.cpuclock.user);
318 break;
319
320 case SCIP_CLOCKTYPE_WALL:
321 #if defined(_WIN32) || defined(_WIN64)
322 clck->data.wallclock.sec -= time(NULL);
323 #else
324 gettimeofday(&tp, NULL);
325 if( tp.tv_usec > clck->data.wallclock.usec ) /*lint !e115 !e40*/
326 {
327 clck->data.wallclock.sec -= (tp.tv_sec + 1); /*lint !e115 !e40*/
328 clck->data.wallclock.usec += (1000000 - tp.tv_usec); /*lint !e115 !e40*/
329 }
330 else
331 {
332 clck->data.wallclock.sec -= tp.tv_sec; /*lint !e115 !e40*/
333 clck->data.wallclock.usec -= tp.tv_usec; /*lint !e115 !e40*/
334 }
335 #endif
336 clck->lasttime = walltime2sec(clck->data.wallclock.sec, clck->data.wallclock.usec);
337 break;
338
339 case SCIP_CLOCKTYPE_DEFAULT:
340 default:
341 SCIPerrorMessage("invalid clock type\n");
342 SCIPABORT();
343 }
344 }
345
346 clck->nruns++;
347 }
348 }
349
350 /** stops measurement of time in the given clock */
SCIPclockStop(SCIP_CLOCK * clck,SCIP_SET * set)351 void SCIPclockStop(
352 SCIP_CLOCK* clck, /**< clock timer */
353 SCIP_SET* set /**< global SCIP settings */
354 )
355 {
356 assert(clck != NULL);
357 assert(set != NULL);
358
359 if( set->time_enabled && clck->enabled )
360 {
361 assert(clck->nruns >= 1);
362
363 clck->nruns--;
364 if( clck->nruns == 0 )
365 {
366 #if defined(_WIN32) || defined(_WIN64)
367 FILETIME creationtime;
368 FILETIME exittime;
369 FILETIME kerneltime;
370 FILETIME usertime;
371 #else
372 struct timeval tp; /*lint !e86*/
373 struct tms now;
374 #endif
375
376 SCIPdebugMessage("stopping clock %p (type %d, usedefault=%u)\n", (void*)clck, clck->clocktype, clck->usedefault);
377
378 switch( clck->clocktype )
379 {
380 case SCIP_CLOCKTYPE_CPU:
381 #if defined(_WIN32) || defined(_WIN64)
382 GetProcessTimes(GetCurrentProcess(), &creationtime, &exittime, &kerneltime, &usertime);
383 clck->data.cpuclock.user += usertime.dwHighDateTime * 42950 + usertime.dwLowDateTime / 100000L;
384 #else
385 (void)times(&now);
386 clck->data.cpuclock.user += now.tms_utime;
387 #endif
388 break;
389
390 case SCIP_CLOCKTYPE_WALL:
391 #if defined(_WIN32) || defined(_WIN64)
392 clck->data.wallclock.sec += time(NULL);
393 #else
394 gettimeofday(&tp, NULL);
395 if( tp.tv_usec + clck->data.wallclock.usec > 1000000 ) /*lint !e115 !e40*/
396 {
397 clck->data.wallclock.sec += (tp.tv_sec + 1); /*lint !e115 !e40*/
398 clck->data.wallclock.usec -= (1000000 - tp.tv_usec); /*lint !e115 !e40*/
399 }
400 else
401 {
402 clck->data.wallclock.sec += tp.tv_sec; /*lint !e115 !e40*/
403 clck->data.wallclock.usec += tp.tv_usec; /*lint !e115 !e40*/
404 }
405 #endif
406 break;
407
408 case SCIP_CLOCKTYPE_DEFAULT:
409 default:
410 SCIPerrorMessage("invalid clock type\n");
411 SCIPABORT();
412 }
413 }
414 }
415 }
416
417 /** returns whether the clock is currently running */
SCIPclockIsRunning(SCIP_CLOCK * clck)418 SCIP_Bool SCIPclockIsRunning(
419 SCIP_CLOCK* clck /**< clock timer */
420 )
421 {
422 assert(clck != NULL);
423
424 return (clck->nruns > 0);
425 }
426
427
428 /** gets the used time of this clock in seconds */
SCIPclockGetTime(SCIP_CLOCK * clck)429 SCIP_Real SCIPclockGetTime(
430 SCIP_CLOCK* clck /**< clock timer */
431 )
432 {
433 SCIP_Real result;
434 assert(clck != NULL);
435 result = 0.0;
436
437 SCIPdebugMessage("getting time of clock %p (type %d, usedefault=%u, nruns=%d)\n",
438 (void*)clck, clck->clocktype, clck->usedefault, clck->nruns);
439
440 if( clck->nruns == 0 )
441 {
442 /* the clock is not running: convert the clocks timer into seconds */
443 switch( clck->clocktype )
444 {
445 case SCIP_CLOCKTYPE_DEFAULT:
446 break;
447 case SCIP_CLOCKTYPE_CPU:
448 result = cputime2sec(clck->data.cpuclock.user);
449 break;
450 case SCIP_CLOCKTYPE_WALL:
451 result = walltime2sec(clck->data.wallclock.sec, clck->data.wallclock.usec);
452 break;
453 default:
454 SCIPerrorMessage("invalid clock type\n");
455 SCIPABORT();
456 result = 0.0; /*lint !e527*/
457 }
458 }
459 else
460 {
461 #if defined(_WIN32) || defined(_WIN64)
462 FILETIME creationtime;
463 FILETIME exittime;
464 FILETIME kerneltime;
465 FILETIME usertime;
466 #else
467 struct timeval tp; /*lint !e86*/
468 struct tms now;
469 #endif
470
471 /* the clock is currently running: we have to add the current time to the clocks timer */
472 switch( clck->clocktype )
473 {
474 case SCIP_CLOCKTYPE_CPU:
475 #if defined(_WIN32) || defined(_WIN64)
476 GetProcessTimes(GetCurrentProcess(), &creationtime, &exittime, &kerneltime, &usertime);
477 result = cputime2sec(clck->data.cpuclock.user + usertime.dwHighDateTime * 42950 + usertime.dwLowDateTime / 100000L);
478 #else
479 (void)times(&now);
480 result = cputime2sec(clck->data.cpuclock.user + now.tms_utime);
481 #endif
482 break;
483 case SCIP_CLOCKTYPE_WALL:
484 #if defined(_WIN32) || defined(_WIN64)
485 result = walltime2sec(clck->data.wallclock.sec + time(NULL), 0);
486 #else
487 gettimeofday(&tp, NULL);
488 if( tp.tv_usec + clck->data.wallclock.usec > 1000000 ) /*lint !e115 !e40*/
489 result = walltime2sec(clck->data.wallclock.sec + tp.tv_sec + 1, /*lint !e115 !e40*/
490 (clck->data.wallclock.usec - 1000000) + tp.tv_usec); /*lint !e115 !e40*/
491 else
492 result = walltime2sec(clck->data.wallclock.sec + tp.tv_sec, /*lint !e115 !e40*/
493 clck->data.wallclock.usec + tp.tv_usec); /*lint !e115 !e40*/
494 #endif
495 break;
496 case SCIP_CLOCKTYPE_DEFAULT:
497 default:
498 SCIPerrorMessage("invalid clock type\n");
499 SCIPABORT();
500 result = 0.0; /*lint !e527*/
501 }
502 }
503
504 clck->lasttime = result;
505 return result;
506 }
507
508 /** gets the last validated time of this clock in seconds */
SCIPclockGetLastTime(SCIP_CLOCK * clck)509 SCIP_Real SCIPclockGetLastTime(
510 SCIP_CLOCK* clck /**< clock timer */
511 )
512 {
513 assert(clck != NULL);
514
515 return clck->lasttime;
516 }
517
518 /** sets the used time of this clock in seconds */
SCIPclockSetTime(SCIP_CLOCK * clck,SCIP_Real sec)519 void SCIPclockSetTime(
520 SCIP_CLOCK* clck, /**< clock timer */
521 SCIP_Real sec /**< time in seconds to set the clock's timer to */
522 )
523 {
524 assert(clck != NULL);
525
526 SCIPdebugMessage("setting time of clock %p (type %d, usedefault=%u, nruns=%d) to %g\n",
527 (void*)clck, clck->clocktype, clck->usedefault, clck->nruns, sec);
528
529 /* if the clock type is not yet set, set it to an arbitrary value to be able to store the number */
530 if( clck->clocktype == SCIP_CLOCKTYPE_DEFAULT )
531 clockSetType(clck, SCIP_CLOCKTYPE_WALL);
532
533 switch( clck->clocktype )
534 {
535 case SCIP_CLOCKTYPE_CPU:
536 sec2cputime(sec, &clck->data.cpuclock.user);
537 break;
538
539 case SCIP_CLOCKTYPE_WALL:
540 sec2walltime(sec, &clck->data.wallclock.sec, &clck->data.wallclock.usec);
541 break;
542
543 case SCIP_CLOCKTYPE_DEFAULT:
544 default:
545 SCIPerrorMessage("invalid clock type\n");
546 SCIPABORT();
547 }
548
549 if( clck->nruns >= 1 )
550 {
551 #if defined(_WIN32) || defined(_WIN64)
552 FILETIME creationtime;
553 FILETIME exittime;
554 FILETIME kerneltime;
555 FILETIME usertime;
556 #else
557 struct timeval tp; /*lint !e86*/
558 struct tms now;
559 #endif
560
561 /* the clock is currently running: we have to subtract the current time from the new timer value */
562 switch( clck->clocktype )
563 {
564 case SCIP_CLOCKTYPE_CPU:
565 #if defined(_WIN32) || defined(_WIN64)
566 GetProcessTimes(GetCurrentProcess(), &creationtime, &exittime, &kerneltime, &usertime);
567 clck->data.cpuclock.user -= usertime.dwHighDateTime * 42950 + usertime.dwLowDateTime / 100000L;
568 #else
569 (void)times(&now);
570 clck->data.cpuclock.user -= now.tms_utime;
571 #endif
572 break;
573
574 case SCIP_CLOCKTYPE_WALL:
575 #if defined(_WIN32) || defined(_WIN64)
576 clck->data.wallclock.sec -= time(NULL);
577 #else
578 gettimeofday(&tp, NULL);
579 if( tp.tv_usec > clck->data.wallclock.usec ) /*lint !e115 !e40*/
580 {
581 clck->data.wallclock.sec -= (tp.tv_sec + 1); /*lint !e115 !e40*/
582 clck->data.wallclock.usec += (1000000 - tp.tv_usec); /*lint !e115 !e40*/
583 }
584 else
585 {
586 clck->data.wallclock.sec -= tp.tv_sec; /*lint !e115 !e40*/
587 clck->data.wallclock.usec -= tp.tv_usec; /*lint !e115 !e40*/
588 }
589 #endif
590 break;
591
592 case SCIP_CLOCKTYPE_DEFAULT:
593 default:
594 SCIPerrorMessage("invalid clock type\n");
595 SCIPABORT();
596 }
597 }
598 }
599
600 /** gets current time of day in seconds (standard time zone) */
SCIPclockGetTimeOfDay(void)601 SCIP_Real SCIPclockGetTimeOfDay(
602 void
603 )
604 {
605 #if defined(_WIN32) || defined(_WIN64)
606 time_t now;
607 now = time(NULL);
608 return (SCIP_Real)(now % (24*3600));
609 #else
610 struct timeval tp; /*lint !e86*/
611
612 gettimeofday(&tp, NULL);
613
614 return (SCIP_Real)(tp.tv_sec % (24*3600)) + (SCIP_Real)tp.tv_usec / 1e+6; /*lint !e40 !e115*/
615 #endif
616 }
617