OR-Tools  9.6
time_limit.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <cstdlib>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 #include <vector>
24 
25 #include "absl/base/port.h"
26 #include "absl/container/flat_hash_map.h"
27 #include "absl/flags/declare.h"
28 #include "absl/synchronization/mutex.h"
29 #include "absl/time/clock.h"
30 #include "ortools/base/logging.h"
31 #include "ortools/base/macros.h"
32 #include "ortools/base/timer.h"
34 #ifdef HAS_PERF_SUBSYSTEM
35 #include "exegesis/exegesis/itineraries/perf_subsystem.h"
36 #endif // HAS_PERF_SUBSYSTEM
37 
42 ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
43 
48 ABSL_DECLARE_FLAG(bool, time_limit_use_instruction_count);
49 
50 namespace operations_research {
51 
104 // TODO(user): The expression "deterministic time" should be replaced with
105 // "number of operations" to avoid confusion with "real" time.
106 class TimeLimit {
107  public:
108  static const double kSafetyBufferSeconds; // See the .cc for the value.
109  static const int kHistorySize;
110 
122  explicit TimeLimit(
123  double limit_in_seconds,
124  double deterministic_limit = std::numeric_limits<double>::infinity(),
125  double instruction_limit = std::numeric_limits<double>::infinity());
126 
127  TimeLimit() : TimeLimit(std::numeric_limits<double>::infinity()) {}
128  TimeLimit(const TimeLimit&) = delete;
129  TimeLimit& operator=(const TimeLimit&) = delete;
130 
135  static std::unique_ptr<TimeLimit> Infinite() {
136  return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
137  std::numeric_limits<double>::infinity(),
138  std::numeric_limits<double>::infinity());
139  }
140 
144  static std::unique_ptr<TimeLimit> FromDeterministicTime(
145  double deterministic_limit) {
146  return std::make_unique<TimeLimit>(std::numeric_limits<double>::infinity(),
147  deterministic_limit,
148  std::numeric_limits<double>::infinity());
149  }
150 
157  // TODO(user): Support adding instruction count limit from parameters.
158  template <typename Parameters>
159  static std::unique_ptr<TimeLimit> FromParameters(
160  const Parameters& parameters) {
161  return std::make_unique<TimeLimit>(parameters.max_time_in_seconds(),
162  parameters.max_deterministic_time(),
163  std::numeric_limits<double>::infinity());
164  }
165 
171  void SetInstructionLimit(double instruction_limit) {
172  instruction_limit_ = instruction_limit;
173  }
174 
179  // TODO(user): Use an exact counter for counting instructions. The
180  // PMU counter returns the instruction count value as double since there may
181  // be sampling issues.
182  double ReadInstructionCounter();
183 
190  bool LimitReached();
191 
204  double GetTimeLeft() const;
205 
212  double GetDeterministicTimeLeft() const {
213  return std::max(0.0, deterministic_limit_ - elapsed_deterministic_time_);
214  }
215 
219  double GetInstructionsLeft();
220 
226  inline void AdvanceDeterministicTime(double deterministic_duration) {
227  DCHECK_LE(0.0, deterministic_duration);
228  elapsed_deterministic_time_ += deterministic_duration;
229  }
230 
240  inline void AdvanceDeterministicTime(double deterministic_duration,
241  const char* counter_name) {
242  AdvanceDeterministicTime(deterministic_duration);
243 #ifndef NDEBUG
244  deterministic_counters_[counter_name] += deterministic_duration;
245 #endif
246  }
247 
251  double GetElapsedTime() const {
252  return 1e-9 * (absl::GetCurrentTimeNanos() - start_ns_);
253  }
254 
261  return elapsed_deterministic_time_;
262  }
263 
274  std::atomic<bool>* external_boolean_as_limit) {
275  external_boolean_as_limit_ = external_boolean_as_limit;
276  }
277 
281  std::atomic<bool>* ExternalBooleanAsLimit() const {
282  return external_boolean_as_limit_;
283  }
284 
289  template <typename Parameters>
290  void ResetLimitFromParameters(const Parameters& parameters);
291  void MergeWithGlobalTimeLimit(TimeLimit* other);
292 
296  void ChangeDeterministicLimit(double new_limit) {
297  deterministic_limit_ = new_limit;
298  }
299 
303  double GetDeterministicLimit() const { return deterministic_limit_; }
304 
308  std::string DebugString() const;
309 
310  private:
311  void ResetTimers(double limit_in_seconds, double deterministic_limit,
312  double instruction_limit);
313 
314  std::string GetInstructionRetiredEventName() const {
315  return "inst_retired:any_p:u";
316  }
317 
318  mutable int64_t start_ns_; // Not const! this is initialized after
319  // instruction counter initialization.
320  int64_t last_ns_;
321  int64_t limit_ns_; // Not const! See the code of LimitReached().
322  const int64_t safety_buffer_ns_;
323  RunningMax<int64_t> running_max_;
324 
325  // Only used when FLAGS_time_limit_use_usertime is true.
326  UserTimer user_timer_;
327  double limit_in_seconds_;
328 
329  double deterministic_limit_;
330  double elapsed_deterministic_time_;
331 
332  std::atomic<bool>* external_boolean_as_limit_;
333 
334 #ifdef HAS_PERF_SUBSYSTEM
335  // PMU counter to help count the instructions.
336  exegesis::PerfSubsystem perf_subsystem_;
337 #endif // HAS_PERF_SUBSYSTEM
338  // Given limit in terms of number of instructions.
339  double instruction_limit_;
340 
341 #ifndef NDEBUG
342  // Contains the values of the deterministic time counters.
343  absl::flat_hash_map<std::string, double> deterministic_counters_;
344 #endif
345 
346  friend class NestedTimeLimit;
347  friend class ParallelTimeLimit;
348 };
349 
350 // Wrapper around TimeLimit to make it thread safe and add Stop() support.
352  public:
354  : time_limit_(time_limit), stopped_boolean_(false) {
355  // We use the one already registered if present or ours otherwise.
356  stopped_ = time_limit->ExternalBooleanAsLimit();
357  if (stopped_ == nullptr) {
358  stopped_ = &stopped_boolean_;
359  time_limit->RegisterExternalBooleanAsLimit(stopped_);
360  }
361  }
362 
364  if (stopped_ == &stopped_boolean_) {
365  time_limit_->RegisterExternalBooleanAsLimit(nullptr);
366  }
367  }
368 
369  bool LimitReached() const {
370  // Note, time_limit_->LimitReached() is not const, and changes internal
371  // state of time_limit_, hence we need a writer's lock.
372  absl::MutexLock lock(&mutex_);
373  return time_limit_->LimitReached();
374  }
375 
376  void Stop() {
377  absl::MutexLock lock(&mutex_);
378  *stopped_ = true;
379  }
380 
381  void UpdateLocalLimit(TimeLimit* local_limit) {
382  absl::MutexLock lock(&mutex_);
383  local_limit->MergeWithGlobalTimeLimit(time_limit_);
384  }
385 
386  void AdvanceDeterministicTime(double deterministic_duration) {
387  absl::MutexLock lock(&mutex_);
388  time_limit_->AdvanceDeterministicTime(deterministic_duration);
389  }
390 
391  double GetTimeLeft() const {
392  absl::ReaderMutexLock lock(&mutex_);
393  return time_limit_->GetTimeLeft();
394  }
395 
397  absl::ReaderMutexLock lock(&mutex_);
398  return time_limit_->GetElapsedDeterministicTime();
399  }
400 
401  std::atomic<bool>* ExternalBooleanAsLimit() const {
402  absl::ReaderMutexLock lock(&mutex_);
403  // We can simply return the "external bool" and remain thread-safe because
404  // it's wrapped in std::atomic.
405  return time_limit_->ExternalBooleanAsLimit();
406  }
407 
408  private:
409  mutable absl::Mutex mutex_;
410  TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
411  std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
412  std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
413 };
414 
446  public:
451  NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
452  double deterministic_limit);
453 
458 
466  template <typename Parameters>
467  static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
468  TimeLimit* time_limit, const Parameters& parameters) {
469  return std::make_unique<NestedTimeLimit>(
470  time_limit, parameters.max_time_in_seconds(),
471  parameters.max_deterministic_time());
472  }
473 
480  TimeLimit* GetTimeLimit() { return &time_limit_; }
481 
482  private:
483  TimeLimit* const base_time_limit_;
484  TimeLimit time_limit_;
485 
486  DISALLOW_COPY_AND_ASSIGN(NestedTimeLimit);
487 };
488 
489 // ################## Implementations below #####################
490 
491 inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit,
492  double instruction_limit)
493  : safety_buffer_ns_(static_cast<int64_t>(kSafetyBufferSeconds * 1e9)),
494  running_max_(kHistorySize),
495  external_boolean_as_limit_(nullptr) {
496  ResetTimers(limit_in_seconds, deterministic_limit, instruction_limit);
497 }
498 
499 inline void TimeLimit::ResetTimers(double limit_in_seconds,
500  double deterministic_limit,
501  double instruction_limit) {
502  elapsed_deterministic_time_ = 0.0;
503  deterministic_limit_ = deterministic_limit;
504  instruction_limit_ = instruction_limit;
505 
506  if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
507  user_timer_.Start();
508  limit_in_seconds_ = limit_in_seconds;
509  }
510 #ifdef HAS_PERF_SUBSYSTEM
511  if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
512  perf_subsystem_.CleanUp();
513  perf_subsystem_.AddEvent(GetInstructionRetiredEventName());
514  perf_subsystem_.StartCollecting();
515  }
516 #endif // HAS_PERF_SUBSYSTEM
517  start_ns_ = absl::GetCurrentTimeNanos();
518  last_ns_ = start_ns_;
519  // Note that duration arithmetic is properly saturated.
520  limit_ns_ = (absl::Seconds(limit_in_seconds) + absl::Nanoseconds(start_ns_)) /
521  absl::Nanoseconds(1);
522 }
523 
524 template <typename Parameters>
525 inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
526  ResetTimers(parameters.max_time_in_seconds(),
527  parameters.max_deterministic_time(),
528  std::numeric_limits<double>::infinity());
529 }
530 
532  if (other == nullptr) return;
533  ResetTimers(
534  std::min(GetTimeLeft(), other->GetTimeLeft()),
536  std::numeric_limits<double>::infinity());
537  if (other->ExternalBooleanAsLimit() != nullptr) {
539  }
540 }
541 
543 #ifdef HAS_PERF_SUBSYSTEM
544  if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
545  return perf_subsystem_.ReadCounters().GetScaledOrDie(
546  GetInstructionRetiredEventName());
547  }
548 #endif // HAS_PERF_SUBSYSTEM
549  return 0;
550 }
551 
552 inline bool TimeLimit::LimitReached() {
553  if (external_boolean_as_limit_ != nullptr &&
554  external_boolean_as_limit_->load()) {
555  return true;
556  }
557 
558  if (GetDeterministicTimeLeft() <= 0.0) {
559  return true;
560  }
561 
562 #ifdef HAS_PERF_SUBSYSTEM
563  if (ReadInstructionCounter() >= instruction_limit_) {
564  return true;
565  }
566 #endif // HAS_PERF_SUBSYSTEM
567 
568  const int64_t current_ns = absl::GetCurrentTimeNanos();
569  running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
570  last_ns_ = current_ns;
571  if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
572  if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
573  // To avoid making many system calls, we only check the user time when
574  // the "absolute" time limit has been reached. Note that the user time
575  // should advance more slowly, so this is correct.
576  const double time_left_s = limit_in_seconds_ - user_timer_.Get();
577  if (time_left_s > kSafetyBufferSeconds) {
578  limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
579  return false;
580  }
581  }
582 
583  // To ensure that future calls to LimitReached() will return true.
584  limit_ns_ = 0;
585  return true;
586  }
587  return false;
588 }
589 
590 inline double TimeLimit::GetTimeLeft() const {
591  if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
592  const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
593  if (delta_ns < 0) return 0.0;
594  if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
595  return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
596  } else {
597  return delta_ns * 1e-9;
598  }
599 }
600 
602  return std::max(instruction_limit_ - ReadInstructionCounter(), 0.0);
603 }
604 
605 } // namespace operations_research
606 
607 #endif // OR_TOOLS_UTIL_TIME_LIMIT_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void Start()
Definition: timer.h:31
double Get() const
Definition: timer.h:45
Provides a way to nest time limits for algorithms where a certain part of the computation is bounded ...
Definition: time_limit.h:445
static std::unique_ptr< NestedTimeLimit > FromBaseTimeLimitAndParameters(TimeLimit *time_limit, const Parameters &parameters)
Creates a time limit object initialized from a base time limit and an object that provides methods ma...
Definition: time_limit.h:467
TimeLimit * GetTimeLimit()
Returns a time limit object that represents the combination of the overall time limit and the part-sp...
Definition: time_limit.h:480
~NestedTimeLimit()
Updates elapsed deterministic time in the base time limit object.
Definition: time_limit.cc:66
NestedTimeLimit(TimeLimit *base_time_limit, double limit_in_seconds, double deterministic_limit)
Creates the nested time limit.
Definition: time_limit.cc:53
std::atomic< bool > * ExternalBooleanAsLimit() const
Definition: time_limit.h:401
void UpdateLocalLimit(TimeLimit *local_limit)
Definition: time_limit.h:381
SharedTimeLimit(TimeLimit *time_limit)
Definition: time_limit.h:353
double GetElapsedDeterministicTime() const
Definition: time_limit.h:396
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:386
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
double GetInstructionsLeft()
Returns the number of instructions left to reach the limit.
Definition: time_limit.h:601
static const double kSafetyBufferSeconds
Definition: time_limit.h:108
std::atomic< bool > * ExternalBooleanAsLimit() const
Returns the current external Boolean limit.
Definition: time_limit.h:281
void ResetLimitFromParameters(const Parameters &parameters)
Sets new time limits.
Definition: time_limit.h:525
double GetDeterministicTimeLeft() const
Returns the remaining deterministic time before LimitReached() returns true due to the deterministic ...
Definition: time_limit.h:212
double GetTimeLeft() const
Returns the time left on this limit, or 0 if the limit was reached (it never returns a negative value...
Definition: time_limit.h:590
void SetInstructionLimit(double instruction_limit)
Sets the instruction limit.
Definition: time_limit.h:171
double ReadInstructionCounter()
Returns the number of instructions executed since the creation of this object.
Definition: time_limit.h:542
static std::unique_ptr< TimeLimit > Infinite()
Creates a time limit object that uses infinite time for wall time, deterministic time and instruction...
Definition: time_limit.h:135
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Registers the external Boolean to check when LimitReached() is called.
Definition: time_limit.h:273
static std::unique_ptr< TimeLimit > FromDeterministicTime(double deterministic_limit)
Creates a time limit object that puts limit only on the deterministic time.
Definition: time_limit.h:144
double GetDeterministicLimit() const
Queries the deterministic time limit.
Definition: time_limit.h:303
std::string DebugString() const
Returns information about the time limit object in a human-readable form.
Definition: time_limit.cc:37
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:552
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
Definition: time_limit.h:159
TimeLimit & operator=(const TimeLimit &)=delete
TimeLimit(const TimeLimit &)=delete
static const int kHistorySize
Definition: time_limit.h:109
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
Definition: time_limit.h:260
void AdvanceDeterministicTime(double deterministic_duration, const char *counter_name)
Advances the deterministic time.
Definition: time_limit.h:240
void MergeWithGlobalTimeLimit(TimeLimit *other)
Definition: time_limit.h:531
double GetElapsedTime() const
Returns the time elapsed in seconds since the construction of this object.
Definition: time_limit.h:251
void ChangeDeterministicLimit(double new_limit)
Overwrites the deterministic time limit with the new value.
Definition: time_limit.h:296
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
Definition: time_limit.h:226
SatParameters parameters
ModelSharedTimeLimit * time_limit
static const int64_t kint64max
Collection of objects used to extend the Constraint Solver library.
ABSL_DECLARE_FLAG(bool, time_limit_use_usertime)
Enables changing the behavior of the TimeLimit class to use -b usertime instead of walltime.