Google OR-Tools v9.11
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-2024 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 OR_TOOLS_UTIL_TIME_LIMIT_H_
15#define OR_TOOLS_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"
33#include "ortools/base/timer.h"
34#include "ortools/base/types.h"
36
41ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
42
43namespace operations_research {
44
92// TODO(user): The expression "deterministic time" should be replaced with
93// "number of operations" to avoid confusion with "real" time.
94class TimeLimit {
95 public:
96 static const double kSafetyBufferSeconds; // See the .cc for the value.
97 static const int kHistorySize;
98
107 explicit TimeLimit(
108 double limit_in_seconds,
109 double deterministic_limit = std::numeric_limits<double>::infinity());
110
111 TimeLimit() : TimeLimit(std::numeric_limits<double>::infinity()) {}
112 TimeLimit(const TimeLimit&) = delete;
113 TimeLimit& operator=(const TimeLimit&) = delete;
114
119 static std::unique_ptr<TimeLimit> Infinite() {
120 return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
121 std::numeric_limits<double>::infinity());
122 }
123
127 static std::unique_ptr<TimeLimit> FromDeterministicTime(
128 double deterministic_limit) {
129 return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
130 deterministic_limit);
131 }
132
139 template <typename Parameters>
140 static std::unique_ptr<TimeLimit> FromParameters(
141 const Parameters& parameters) {
142 return std::make_unique<TimeLimit>(parameters.max_time_in_seconds(),
143 parameters.max_deterministic_time());
144 }
145
152 bool LimitReached();
153
166 double GetTimeLeft() const;
167
175 return std::max(0.0, deterministic_limit_ - elapsed_deterministic_time_);
176 }
177
183 inline void AdvanceDeterministicTime(double deterministic_duration) {
184 DCHECK_LE(0.0, deterministic_duration);
185 elapsed_deterministic_time_ += deterministic_duration;
186 }
187
197 inline void AdvanceDeterministicTime(double deterministic_duration,
198 const char* counter_name) {
199 AdvanceDeterministicTime(deterministic_duration);
200#ifndef NDEBUG
201 deterministic_counters_[counter_name] += deterministic_duration;
202#endif
203 }
204
208 double GetElapsedTime() const {
209 return 1e-9 * (absl::GetCurrentTimeNanos() - start_ns_);
210 }
211
218 return elapsed_deterministic_time_;
219 }
220
231 std::atomic<bool>* external_boolean_as_limit) {
232 external_boolean_as_limit_ = external_boolean_as_limit;
233 }
234
244 std::atomic<bool>* external_boolean_as_limit) {
245 secondary_external_boolean_as_limit_ = external_boolean_as_limit;
246 }
247
251 std::atomic<bool>* ExternalBooleanAsLimit() const {
252 return external_boolean_as_limit_;
253 }
254
259 template <typename Parameters>
260 void ResetLimitFromParameters(const Parameters& parameters);
261
268
272 void ChangeDeterministicLimit(double new_limit) {
273 deterministic_limit_ = new_limit;
274 }
275
279 double GetDeterministicLimit() const { return deterministic_limit_; }
280
284 std::string DebugString() const;
285
286 private:
287 void ResetTimers(double limit_in_seconds, double deterministic_limit);
288
289 mutable int64_t start_ns_; // Not const! this is initialized after
290 // instruction counter initialization.
291 int64_t last_ns_;
292 int64_t limit_ns_; // Not const! See the code of LimitReached().
293 const int64_t safety_buffer_ns_;
294 RunningMax<int64_t> running_max_;
295
296 // Only used when FLAGS_time_limit_use_usertime is true.
297 UserTimer user_timer_;
298 double limit_in_seconds_;
299
300 double deterministic_limit_;
301 double elapsed_deterministic_time_;
302
303 std::atomic<bool>* external_boolean_as_limit_ = nullptr;
304 std::atomic<bool>* secondary_external_boolean_as_limit_ = nullptr;
305
306#ifndef NDEBUG
307 // Contains the values of the deterministic time counters.
308 absl::flat_hash_map<std::string, double> deterministic_counters_;
309#endif
310
311 friend class NestedTimeLimit;
312 friend class ParallelTimeLimit;
313};
314
315// Wrapper around TimeLimit to make it thread safe and add Stop() support.
317 public:
319 : time_limit_(time_limit), stopped_boolean_(false) {
320 // We use the one already registered if present or ours otherwise.
321 stopped_ = time_limit->ExternalBooleanAsLimit();
322 if (stopped_ == nullptr) {
323 stopped_ = &stopped_boolean_;
324 time_limit->RegisterExternalBooleanAsLimit(stopped_);
325 }
326 }
327
329 if (stopped_ == &stopped_boolean_) {
330 time_limit_->RegisterExternalBooleanAsLimit(nullptr);
331 }
332 }
333
334 bool LimitReached() const {
335 // Note, time_limit_->LimitReached() is not const, and changes internal
336 // state of time_limit_, hence we need a writer's lock.
337 absl::MutexLock lock(&mutex_);
338 return time_limit_->LimitReached();
339 }
340
341 void Stop() {
342 absl::MutexLock lock(&mutex_);
343 *stopped_ = true;
344 }
345
346 void UpdateLocalLimit(TimeLimit* local_limit) {
347 absl::MutexLock lock(&mutex_);
348 local_limit->MergeWithGlobalTimeLimit(time_limit_);
349 }
350
351 void AdvanceDeterministicTime(double deterministic_duration) {
352 absl::MutexLock lock(&mutex_);
353 time_limit_->AdvanceDeterministicTime(deterministic_duration);
354 }
355
356 double GetTimeLeft() const {
357 absl::ReaderMutexLock lock(&mutex_);
358 return time_limit_->GetTimeLeft();
359 }
360
362 absl::ReaderMutexLock lock(&mutex_);
363 return time_limit_->GetElapsedDeterministicTime();
364 }
365
366 std::atomic<bool>* ExternalBooleanAsLimit() const {
367 absl::ReaderMutexLock lock(&mutex_);
368 // We can simply return the "external bool" and remain thread-safe because
369 // it's wrapped in std::atomic.
370 return time_limit_->ExternalBooleanAsLimit();
371 }
372
373 private:
374 mutable absl::Mutex mutex_;
375 TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
376 std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
377 std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
378};
379
411 public:
416 NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
417 double deterministic_limit);
418
419 // This type is neither copyable nor movable.
422
427
435 template <typename Parameters>
436 static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
437 TimeLimit* time_limit, const Parameters& parameters) {
438 return std::make_unique<NestedTimeLimit>(
439 time_limit, parameters.max_time_in_seconds(),
440 parameters.max_deterministic_time());
441 }
442
449 TimeLimit* GetTimeLimit() { return &time_limit_; }
450
451 private:
452 TimeLimit* const base_time_limit_;
453 TimeLimit time_limit_;
454};
455
456// ################## Implementations below #####################
457
458inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit)
459 : safety_buffer_ns_(static_cast<int64_t>(kSafetyBufferSeconds * 1e9)),
460 running_max_(kHistorySize),
461 external_boolean_as_limit_(nullptr) {
462 ResetTimers(limit_in_seconds, deterministic_limit);
463}
464
465inline void TimeLimit::ResetTimers(double limit_in_seconds,
466 double deterministic_limit) {
467 elapsed_deterministic_time_ = 0.0;
468 deterministic_limit_ = deterministic_limit;
469
470 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
471 user_timer_.Start();
472 limit_in_seconds_ = limit_in_seconds;
473 }
474 start_ns_ = absl::GetCurrentTimeNanos();
475 last_ns_ = start_ns_;
476 // Note that duration arithmetic is properly saturated.
477 limit_ns_ = (absl::Seconds(limit_in_seconds) + absl::Nanoseconds(start_ns_)) /
478 absl::Nanoseconds(1);
479}
480
481template <typename Parameters>
482inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
483 ResetTimers(parameters.max_time_in_seconds(),
484 parameters.max_deterministic_time());
485}
486
488 if (other == nullptr) return;
489 ResetTimers(
490 std::min(GetTimeLeft(), other->GetTimeLeft()),
492 if (other->ExternalBooleanAsLimit() != nullptr) {
494 }
495}
496
498 if (external_boolean_as_limit_ != nullptr &&
499 external_boolean_as_limit_->load()) {
500 return true;
501 }
502 if (secondary_external_boolean_as_limit_ != nullptr &&
503 secondary_external_boolean_as_limit_->load()) {
504 return true;
505 }
506
507 if (GetDeterministicTimeLeft() <= 0.0) {
508 return true;
509 }
510
511 const int64_t current_ns = absl::GetCurrentTimeNanos();
512 running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
513 last_ns_ = current_ns;
514 if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
515 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
516 // To avoid making many system calls, we only check the user time when
517 // the "absolute" time limit has been reached. Note that the user time
518 // should advance more slowly, so this is correct.
519 const double time_left_s = limit_in_seconds_ - user_timer_.Get();
520 if (time_left_s > kSafetyBufferSeconds) {
521 limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
522 return false;
523 }
524 }
525
526 // To ensure that future calls to LimitReached() will return true.
527 limit_ns_ = 0;
528 return true;
529 }
530 return false;
531}
532
533inline double TimeLimit::GetTimeLeft() const {
534 if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
535 const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
536 if (delta_ns < 0) return 0.0;
537 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
538 return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
539 } else {
540 return delta_ns * 1e-9;
541 }
542}
543
544} // namespace operations_research
545
546#endif // OR_TOOLS_UTIL_TIME_LIMIT_H_
double Get() const
Definition timer.h:46
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
static std::unique_ptr< NestedTimeLimit > FromBaseTimeLimitAndParameters(TimeLimit *time_limit, const Parameters &parameters)
Definition time_limit.h:436
NestedTimeLimit(const NestedTimeLimit &)=delete
This type is neither copyable nor movable.
NestedTimeLimit(TimeLimit *base_time_limit, double limit_in_seconds, double deterministic_limit)
Definition time_limit.cc:50
NestedTimeLimit & operator=(const NestedTimeLimit &)=delete
void Add(Number value)
Processes a new element from the stream.
Wrapper around TimeLimit to make it thread safe and add Stop() support.
Definition time_limit.h:316
void UpdateLocalLimit(TimeLimit *local_limit)
Definition time_limit.h:346
void AdvanceDeterministicTime(double deterministic_duration)
Definition time_limit.h:351
std::atomic< bool > * ExternalBooleanAsLimit() const
Definition time_limit.h:366
SharedTimeLimit(TimeLimit *time_limit)
Definition time_limit.h:318
TimeLimit & operator=(const TimeLimit &)=delete
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Definition time_limit.h:140
TimeLimit(const TimeLimit &)=delete
double GetDeterministicLimit() const
Definition time_limit.h:279
void AdvanceDeterministicTime(double deterministic_duration)
Definition time_limit.h:183
std::atomic< bool > * ExternalBooleanAsLimit() const
Definition time_limit.h:251
void MergeWithGlobalTimeLimit(TimeLimit *other)
Definition time_limit.h:487
double GetElapsedDeterministicTime() const
Definition time_limit.h:217
static std::unique_ptr< TimeLimit > Infinite()
Definition time_limit.h:119
void AdvanceDeterministicTime(double deterministic_duration, const char *counter_name)
Definition time_limit.h:197
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Definition time_limit.h:230
void RegisterSecondaryExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Definition time_limit.h:243
double GetDeterministicTimeLeft() const
Definition time_limit.h:174
static const int kHistorySize
Definition time_limit.h:97
static std::unique_ptr< TimeLimit > FromDeterministicTime(double deterministic_limit)
Definition time_limit.h:127
static const double kSafetyBufferSeconds
static constants.
Definition time_limit.h:96
void ResetLimitFromParameters(const Parameters &parameters)
Definition time_limit.h:482
void ChangeDeterministicLimit(double new_limit)
Definition time_limit.h:272
std::string DebugString() const
Definition time_limit.cc:34
SatParameters parameters
time_limit
Definition solve.cc:22
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
ABSL_DECLARE_FLAG(bool, time_limit_use_usertime)
static const int64_t kint64max
Definition types.h:32