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