Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
time_limit.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_UTIL_TIME_LIMIT_H_
15#define ORTOOLS_UTIL_TIME_LIMIT_H_
16
17#include <algorithm>
18#include <atomic>
19#include <cstdint>
20#include <limits>
21#include <memory>
22#include <string>
23#include <vector>
24
25#include "absl/base/thread_annotations.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/flags/declare.h"
28#include "absl/flags/flag.h"
29#include "absl/log/check.h"
30#include "absl/synchronization/mutex.h"
31#include "absl/time/clock.h"
32#include "absl/time/time.h"
34#include "ortools/base/timer.h"
35#include "ortools/base/types.h"
37
38#ifndef SWIG
43OR_DLL ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
44#endif // SWIG
45
46namespace operations_research {
47
95// TODO(user): The expression "deterministic time" should be replaced with
96// "number of operations" to avoid confusion with "real" time.
98 public:
99 static const double kSafetyBufferSeconds; // See the .cc for the value.
100 static const int kHistorySize;
101
110 explicit TimeLimit(
111 double limit_in_seconds,
112 double deterministic_limit = std::numeric_limits<double>::infinity());
113
114 TimeLimit() : TimeLimit(std::numeric_limits<double>::infinity()) {}
115 TimeLimit(const TimeLimit&) = delete;
116 TimeLimit& operator=(const TimeLimit&) = delete;
117
122 static std::unique_ptr<TimeLimit> Infinite() {
123 return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
124 std::numeric_limits<double>::infinity());
125 }
126
130 static std::unique_ptr<TimeLimit> FromDeterministicTime(
131 double deterministic_limit) {
132 return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
133 deterministic_limit);
134 }
135
142 template <typename Parameters>
143 static std::unique_ptr<TimeLimit> FromParameters(
144 const Parameters& parameters) {
145 return std::make_unique<TimeLimit>(parameters.max_time_in_seconds(),
146 parameters.max_deterministic_time());
147 }
148
155 bool LimitReached();
156
169 double GetTimeLeft() const;
170
178 return std::max(0.0, deterministic_limit_ - elapsed_deterministic_time_);
179 }
180
186 inline void AdvanceDeterministicTime(double deterministic_duration) {
187 DCHECK_LE(0.0, deterministic_duration);
188 elapsed_deterministic_time_ += deterministic_duration;
189 }
190
200 inline void AdvanceDeterministicTime(double deterministic_duration,
201 const char* counter_name) {
202 AdvanceDeterministicTime(deterministic_duration);
203#ifndef NDEBUG
204 deterministic_counters_[counter_name] += deterministic_duration;
205#endif
206 }
207
211 double GetElapsedTime() const {
212 return 1e-9 * (absl::GetCurrentTimeNanos() - start_ns_);
213 }
214
221 return elapsed_deterministic_time_;
222 }
223
234 std::atomic<bool>* external_boolean_as_limit) {
235 external_boolean_as_limit_ = external_boolean_as_limit;
236 }
237
247 std::atomic<bool>* external_boolean_as_limit) {
248 secondary_external_boolean_as_limit_ = external_boolean_as_limit;
249 }
250
254 std::atomic<bool>* ExternalBooleanAsLimit() const {
255 return external_boolean_as_limit_;
256 }
257
262 template <typename Parameters>
263 void ResetLimitFromParameters(const Parameters& parameters);
264
270 void MergeWithGlobalTimeLimit(const TimeLimit* other);
271
275 void ChangeDeterministicLimit(double new_limit) {
276 deterministic_limit_ = new_limit;
277 }
278
282 double GetDeterministicLimit() const { return deterministic_limit_; }
283
290 void ResetHistory() { running_max_.Reset(); }
291
295 std::string DebugString() const;
296
297 private:
298 void ResetTimers(double limit_in_seconds, double deterministic_limit);
299
300 mutable int64_t start_ns_; // Not const! this is initialized after
301 // instruction counter initialization.
302 int64_t last_ns_;
303 int64_t limit_ns_; // Not const! See the code of LimitReached().
304 const int64_t safety_buffer_ns_;
305 RunningMax<int64_t> running_max_;
306
307 // Only used when FLAGS_time_limit_use_usertime is true.
308 UserTimer user_timer_;
309 double limit_in_seconds_;
310
311 double deterministic_limit_;
312 double elapsed_deterministic_time_;
313
314 std::atomic<bool>* external_boolean_as_limit_ = nullptr;
315 std::atomic<bool>* secondary_external_boolean_as_limit_ = nullptr;
316
317#ifndef NDEBUG
318 // Contains the values of the deterministic time counters.
319 absl::flat_hash_map<std::string, double> deterministic_counters_;
320#endif
321
322 friend class NestedTimeLimit;
323 friend class ParallelTimeLimit;
324};
325
326// Wrapper around TimeLimit to make it thread safe and add Stop() support.
328 public:
329 explicit SharedTimeLimit(TimeLimit* time_limit)
330 : time_limit_(time_limit), stopped_boolean_(false) {
331 // We use the one already registered if present or ours otherwise.
332 stopped_ = time_limit->ExternalBooleanAsLimit();
333 if (stopped_ == nullptr) {
334 stopped_ = &stopped_boolean_;
335 time_limit->RegisterExternalBooleanAsLimit(stopped_);
336 }
337 }
338
340 if (stopped_ == &stopped_boolean_) {
341 time_limit_->RegisterExternalBooleanAsLimit(nullptr);
342 }
343 }
344
345 bool LimitReached() const {
346 // Note, time_limit_->LimitReached() is not const, and changes internal
347 // state of time_limit_, hence we need a writer's lock.
348 absl::MutexLock lock(mutex_);
349 return time_limit_->LimitReached();
350 }
351
352 void Stop() {
353 absl::MutexLock lock(mutex_);
354 *stopped_ = true;
355 }
356
357 void UpdateLocalLimit(TimeLimit* local_limit) {
358 absl::MutexLock lock(mutex_);
359 local_limit->MergeWithGlobalTimeLimit(time_limit_);
360 }
361
362 void AdvanceDeterministicTime(double deterministic_duration) {
363 absl::MutexLock lock(mutex_);
364 time_limit_->AdvanceDeterministicTime(deterministic_duration);
365 }
366
367 double GetTimeLeft() const {
368 absl::ReaderMutexLock lock(mutex_);
369 return time_limit_->GetTimeLeft();
370 }
371
373 absl::ReaderMutexLock lock(mutex_);
374 return time_limit_->GetElapsedDeterministicTime();
375 }
376
377 std::atomic<bool>* ExternalBooleanAsLimit() const {
378 absl::ReaderMutexLock lock(mutex_);
379 // We can simply return the "external bool" and remain thread-safe because
380 // it's wrapped in std::atomic.
381 return time_limit_->ExternalBooleanAsLimit();
382 }
383
384 private:
385 mutable absl::Mutex mutex_;
386 TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
387 std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
388 std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
389};
390
422 public:
427 NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
428 double deterministic_limit);
429
430 // This type is neither copyable nor movable.
433
438
446 template <typename Parameters>
447 static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
448 TimeLimit* time_limit, const Parameters& parameters) {
449 return std::make_unique<NestedTimeLimit>(
450 time_limit, parameters.max_time_in_seconds(),
451 parameters.max_deterministic_time());
452 }
453
460 TimeLimit* GetTimeLimit() { return &time_limit_; }
461
462 private:
463 TimeLimit* const base_time_limit_;
464 TimeLimit time_limit_;
465};
466
468 public:
470 : time_limit_(time_limit), frequency_(N) {}
471
473 if (count_++ == frequency_) {
474 if (time_limit_->LimitReached()) {
475 stopped_ = true;
476 return true;
477 }
478 count_ = 0;
479 }
480 return stopped_;
481 }
482
483 private:
484 TimeLimit* time_limit_;
485 bool stopped_ = false;
486 int count_ = 0;
487 const int frequency_;
488};
489
490// ################## Implementations below #####################
491
492inline void TimeLimit::ResetTimers(double limit_in_seconds,
493 double deterministic_limit) {
494 elapsed_deterministic_time_ = 0.0;
495 deterministic_limit_ = deterministic_limit;
496
497 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
498 user_timer_.Start();
499 limit_in_seconds_ = limit_in_seconds;
500 }
501 start_ns_ = absl::GetCurrentTimeNanos();
502 last_ns_ = start_ns_;
503 // Note that duration arithmetic is properly saturated.
504 limit_ns_ = (absl::Seconds(limit_in_seconds) + absl::Nanoseconds(start_ns_)) /
505 absl::Nanoseconds(1);
506}
507
508template <typename Parameters>
509inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
510 ResetTimers(parameters.max_time_in_seconds(),
511 parameters.max_deterministic_time());
512}
513
515 if (other == nullptr) return;
516 ResetTimers(
517 std::min(GetTimeLeft(), other->GetTimeLeft()),
519 if (other->ExternalBooleanAsLimit() != nullptr) {
521 }
522}
523
525 if (external_boolean_as_limit_ != nullptr &&
526 external_boolean_as_limit_->load()) {
527 return true;
528 }
529 if (secondary_external_boolean_as_limit_ != nullptr &&
530 secondary_external_boolean_as_limit_->load()) {
531 return true;
532 }
533
534 if (GetDeterministicTimeLeft() <= 0.0) {
535 return true;
536 }
537
538 const int64_t current_ns = absl::GetCurrentTimeNanos();
539 running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
540 last_ns_ = current_ns;
541 if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
542 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
543 // To avoid making many system calls, we only check the user time when
544 // the "absolute" time limit has been reached. Note that the user time
545 // should advance more slowly, so this is correct.
546 const double time_left_s = limit_in_seconds_ - user_timer_.Get();
547 if (time_left_s > kSafetyBufferSeconds) {
548 limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
549 return false;
550 }
551 }
552
553 // To ensure that future calls to LimitReached() will return true.
554 limit_ns_ = 0;
555 return true;
556 }
557 return false;
558}
559
560inline double TimeLimit::GetTimeLeft() const {
561 if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
562 const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
563 if (delta_ns < 0) return 0.0;
564 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
565 return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
566 } else {
567 return delta_ns * 1e-9;
568 }
569}
570
571} // namespace operations_research
572
573#endif // ORTOOLS_UTIL_TIME_LIMIT_H_
#define OR_DLL
Definition base_export.h:27
void Start()
Definition timer.h:30
static std::unique_ptr< NestedTimeLimit > FromBaseTimeLimitAndParameters(TimeLimit *time_limit, const Parameters &parameters)
Definition time_limit.h:447
NestedTimeLimit(const NestedTimeLimit &)=delete
NestedTimeLimit(TimeLimit *base_time_limit, double limit_in_seconds, double deterministic_limit)
Definition time_limit.cc:62
NestedTimeLimit & operator=(const NestedTimeLimit &)=delete
void UpdateLocalLimit(TimeLimit *local_limit)
Definition time_limit.h:357
void AdvanceDeterministicTime(double deterministic_duration)
Definition time_limit.h:362
std::atomic< bool > * ExternalBooleanAsLimit() const
Definition time_limit.h:377
SharedTimeLimit(TimeLimit *time_limit)
Definition time_limit.h:329
TimeLimitCheckEveryNCalls(int N, TimeLimit *time_limit)
Definition time_limit.h:469
TimeLimit & operator=(const TimeLimit &)=delete
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Definition time_limit.h:143
TimeLimit(const TimeLimit &)=delete
TimeLimit(double limit_in_seconds, double deterministic_limit=std::numeric_limits< double >::infinity())
Definition time_limit.cc:39
void MergeWithGlobalTimeLimit(const TimeLimit *other)
Definition time_limit.h:514
double GetDeterministicLimit() const
Definition time_limit.h:282
void AdvanceDeterministicTime(double deterministic_duration)
Definition time_limit.h:186
std::atomic< bool > * ExternalBooleanAsLimit() const
Definition time_limit.h:254
double GetElapsedDeterministicTime() const
Definition time_limit.h:220
static std::unique_ptr< TimeLimit > Infinite()
Definition time_limit.h:122
void AdvanceDeterministicTime(double deterministic_duration, const char *counter_name)
Definition time_limit.h:200
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Definition time_limit.h:233
void RegisterSecondaryExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Definition time_limit.h:246
double GetDeterministicTimeLeft() const
Definition time_limit.h:177
static const int kHistorySize
Definition time_limit.h:100
static std::unique_ptr< TimeLimit > FromDeterministicTime(double deterministic_limit)
Definition time_limit.h:130
static const double kSafetyBufferSeconds
Definition time_limit.h:99
void ResetLimitFromParameters(const Parameters &parameters)
Definition time_limit.h:509
void ChangeDeterministicLimit(double new_limit)
Definition time_limit.h:275
OR-Tools root namespace.
STL namespace.
OR_DLL ABSL_DECLARE_FLAG(bool, time_limit_use_usertime)
WallTimer UserTimer
Definition timer.h:65
static const int64_t kint64max
Definition types.h:32