C++ Reference

C++ Reference: CP-SAT

time_limit.h
Go to the documentation of this file.
1 // Copyright 2010-2018 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 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/memory/memory.h"
26 #include "absl/synchronization/mutex.h"
27 #include "absl/time/clock.h"
28 #include "ortools/base/commandlineflags.h"
29 #include "ortools/base/logging.h"
30 #include "ortools/base/macros.h"
31 #include "ortools/base/timer.h"
32 #include "ortools/util/running_stat.h"
33 #ifdef HAS_PERF_SUBSYSTEM
34 #include "exegesis/exegesis/itineraries/perf_subsystem.h"
35 #endif // HAS_PERF_SUBSYSTEM
36 
41 DECLARE_bool(time_limit_use_usertime);
42 
47 DECLARE_bool(time_limit_use_instruction_count);
48 
49 namespace operations_research {
50 
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 absl::make_unique<TimeLimit>(
137  std::numeric_limits<double>::infinity(),
138  std::numeric_limits<double>::infinity(),
139  std::numeric_limits<double>::infinity());
140  }
141 
145  static std::unique_ptr<TimeLimit> FromDeterministicTime(
146  double deterministic_limit) {
147  return absl::make_unique<TimeLimit>(
148  std::numeric_limits<double>::infinity(), deterministic_limit,
149  std::numeric_limits<double>::infinity());
150  }
151 
158  // TODO(user): Support adding instruction count limit from parameters.
159  template <typename Parameters>
160  static std::unique_ptr<TimeLimit> FromParameters(
161  const Parameters& parameters) {
162  return absl::make_unique<TimeLimit>(
163  parameters.max_time_in_seconds(), parameters.max_deterministic_time(),
164  std::numeric_limits<double>::infinity());
165  }
166 
172  void SetInstructionLimit(double instruction_limit) {
173  instruction_limit_ = instruction_limit;
174  }
175 
180  // TODO(user): Use an exact counter for counting instructions. The
181  // PMU counter returns the instruction count value as double since there may
182  // be sampling issues.
183  double ReadInstructionCounter();
184 
191  bool LimitReached();
192 
205  double GetTimeLeft() const;
206 
213  double GetDeterministicTimeLeft() const {
214  return std::max(0.0, deterministic_limit_ - elapsed_deterministic_time_);
215  }
216 
220  double GetInstructionsLeft();
221 
227  inline void AdvanceDeterministicTime(double deterministic_duration) {
228  DCHECK_LE(0.0, deterministic_duration);
229  elapsed_deterministic_time_ += deterministic_duration;
230  }
231 
241  inline void AdvanceDeterministicTime(double deterministic_duration,
242  const char* counter_name) {
243  AdvanceDeterministicTime(deterministic_duration);
244 #ifndef NDEBUG
245  deterministic_counters_[counter_name] += deterministic_duration;
246 #endif
247  }
248 
252  double GetElapsedTime() const {
253  return 1e-9 * (absl::GetCurrentTimeNanos() - start_ns_);
254  }
255 
262  return elapsed_deterministic_time_;
263  }
264 
276  std::atomic<bool>* external_boolean_as_limit) {
277  external_boolean_as_limit_ = external_boolean_as_limit;
278  }
279 
283  std::atomic<bool>* ExternalBooleanAsLimit() const {
284  return external_boolean_as_limit_;
285  }
286 
291  template <typename Parameters>
292  void ResetLimitFromParameters(const Parameters& parameters);
293  void MergeWithGlobalTimeLimit(TimeLimit* other);
294 
298  std::string DebugString() const;
299 
300  private:
301  void ResetTimers(double limit_in_seconds, double deterministic_limit,
302  double instruction_limit);
303 
304  std::string GetInstructionRetiredEventName() const {
305  return "inst_retired:any_p:u";
306  }
307 
308  mutable int64 start_ns_; // Not const! this is initialized after instruction
309  // counter initialization.
310  int64 last_ns_;
311  int64 limit_ns_; // Not const! See the code of LimitReached().
312  const int64 safety_buffer_ns_;
313  RunningMax<int64> running_max_;
314 
315  // Only used when FLAGS_time_limit_use_usertime is true.
316  UserTimer user_timer_;
317  double limit_in_seconds_;
318 
319  double deterministic_limit_;
320  double elapsed_deterministic_time_;
321 
322  std::atomic<bool>* external_boolean_as_limit_;
323 
324 #ifdef HAS_PERF_SUBSYSTEM
325  // PMU counter to help count the instructions.
326  exegesis::PerfSubsystem perf_subsystem_;
327 #endif // HAS_PERF_SUBSYSTEM
328  // Given limit in terms of number of instructions.
329  double instruction_limit_;
330 
331 #ifndef NDEBUG
332  // Contains the values of the deterministic time counters.
333  absl::flat_hash_map<std::string, double> deterministic_counters_;
334 #endif
335 
336  friend class NestedTimeLimit;
337  friend class ParallelTimeLimit;
338 };
339 
340 // Wrapper around TimeLimit to make it thread safe and add Stop() support.
342  public:
343  explicit SharedTimeLimit(TimeLimit* time_limit)
344  : time_limit_(time_limit), stopped_boolean_(false) {
345  // We use the one already registered if present or ours otherwise.
346  stopped_ = time_limit->ExternalBooleanAsLimit();
347  if (stopped_ == nullptr) {
348  stopped_ = &stopped_boolean_;
349  time_limit->RegisterExternalBooleanAsLimit(stopped_);
350  }
351  }
352 
354  if (stopped_ == &stopped_boolean_) {
355  time_limit_->RegisterExternalBooleanAsLimit(nullptr);
356  }
357  }
358 
359  bool LimitReached() const {
360  // Note, time_limit_->LimitReached() is not const, and changes internal
361  // state of time_limit_, hence we need a writer's lock.
362  absl::MutexLock lock(&mutex_);
363  return time_limit_->LimitReached();
364  }
365 
366  void Stop() {
367  absl::MutexLock lock(&mutex_);
368  *stopped_ = true;
369  }
370 
371  void UpdateLocalLimit(TimeLimit* local_limit) {
372  absl::MutexLock lock(&mutex_);
373  local_limit->MergeWithGlobalTimeLimit(time_limit_);
374  }
375 
376  void AdvanceDeterministicTime(double deterministic_duration) {
377  absl::MutexLock lock(&mutex_);
378  time_limit_->AdvanceDeterministicTime(deterministic_duration);
379  }
380 
381  double GetTimeLeft() const {
382  absl::ReaderMutexLock lock(&mutex_);
383  return time_limit_->GetTimeLeft();
384  }
385 
387  absl::ReaderMutexLock lock(&mutex_);
388  return time_limit_->GetElapsedDeterministicTime();
389  }
390 
391  private:
392  mutable absl::Mutex mutex_;
393  TimeLimit* time_limit_ GUARDED_BY(mutex_);
394  std::atomic<bool> stopped_boolean_ GUARDED_BY(mutex_);
395  std::atomic<bool>* stopped_ GUARDED_BY(mutex_);
396 };
397 
429  public:
434  NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
435  double deterministic_limit);
436 
441 
449  template <typename Parameters>
450  static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
451  TimeLimit* time_limit, const Parameters& parameters) {
452  return absl::make_unique<NestedTimeLimit>(
453  time_limit, parameters.max_time_in_seconds(),
454  parameters.max_deterministic_time());
455  }
456 
463  TimeLimit* GetTimeLimit() { return &time_limit_; }
464 
465  private:
466  TimeLimit* const base_time_limit_;
467  TimeLimit time_limit_;
468 
469  DISALLOW_COPY_AND_ASSIGN(NestedTimeLimit);
470 };
471 
472 // ################## Implementations below #####################
473 
474 inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit,
475  double instruction_limit)
476  : safety_buffer_ns_(static_cast<int64>(kSafetyBufferSeconds * 1e9)),
477  running_max_(kHistorySize),
478  external_boolean_as_limit_(nullptr) {
479  ResetTimers(limit_in_seconds, deterministic_limit, instruction_limit);
480 }
481 
482 inline void TimeLimit::ResetTimers(double limit_in_seconds,
483  double deterministic_limit,
484  double instruction_limit) {
485  if (external_boolean_as_limit_ != nullptr) {
486  *external_boolean_as_limit_ = false;
487  }
488  elapsed_deterministic_time_ = 0.0;
489  deterministic_limit_ = deterministic_limit;
490  instruction_limit_ = instruction_limit;
491 
492  if (FLAGS_time_limit_use_usertime) {
493  user_timer_.Start();
494  limit_in_seconds_ = limit_in_seconds;
495  }
496 #ifdef HAS_PERF_SUBSYSTEM
497  if (FLAGS_time_limit_use_instruction_count) {
498  perf_subsystem_.CleanUp();
499  perf_subsystem_.AddEvent(GetInstructionRetiredEventName());
500  perf_subsystem_.StartCollecting();
501  }
502 #endif // HAS_PERF_SUBSYSTEM
503  start_ns_ = absl::GetCurrentTimeNanos();
504  last_ns_ = start_ns_;
505  limit_ns_ = limit_in_seconds >= 1e-9 * (kint64max - start_ns_)
506  ? kint64max
507  : static_cast<int64>(limit_in_seconds * 1e9) + start_ns_;
508 }
509 
510 template <typename Parameters>
511 inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
512  ResetTimers(parameters.max_time_in_seconds(),
513  parameters.max_deterministic_time(),
514  std::numeric_limits<double>::infinity());
515 }
516 
518  if (other == nullptr) return;
519  ResetTimers(
520  std::min(GetTimeLeft(), other->GetTimeLeft()),
522  std::numeric_limits<double>::infinity());
523  if (other->ExternalBooleanAsLimit() != nullptr) {
525  }
526 }
527 
529 #ifdef HAS_PERF_SUBSYSTEM
530  if (FLAGS_time_limit_use_instruction_count) {
531  return perf_subsystem_.ReadCounters().GetScaledOrDie(
532  GetInstructionRetiredEventName());
533  }
534 #endif // HAS_PERF_SUBSYSTEM
535  return 0;
536 }
537 
538 inline bool TimeLimit::LimitReached() {
539  if (external_boolean_as_limit_ != nullptr &&
540  external_boolean_as_limit_->load()) {
541  return true;
542  }
543 
544  if (GetDeterministicTimeLeft() <= 0.0) {
545  return true;
546  }
547 
548 #ifdef HAS_PERF_SUBSYSTEM
549  if (ReadInstructionCounter() >= instruction_limit_) {
550  return true;
551  }
552 #endif // HAS_PERF_SUBSYSTEM
553 
554  const int64 current_ns = absl::GetCurrentTimeNanos();
555  running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
556  last_ns_ = current_ns;
557  if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
558  if (FLAGS_time_limit_use_usertime) {
559  // To avoid making many system calls, we only check the user time when
560  // the "absolute" time limit has been reached. Note that the user time
561  // should advance more slowly, so this is correct.
562  const double time_left_s = limit_in_seconds_ - user_timer_.Get();
563  if (time_left_s > kSafetyBufferSeconds) {
564  limit_ns_ = static_cast<int64>(time_left_s * 1e9) + last_ns_;
565  return false;
566  }
567  }
568 
569  // To ensure that future calls to LimitReached() will return true.
570  limit_ns_ = 0;
571  return true;
572  }
573  return false;
574 }
575 
576 inline double TimeLimit::GetTimeLeft() const {
577  if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
578  const int64 delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
579  if (delta_ns < 0) return 0.0;
580  if (FLAGS_time_limit_use_usertime) {
581  return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
582  } else {
583  return delta_ns * 1e-9;
584  }
585 }
586 
588  return std::max(instruction_limit_ - ReadInstructionCounter(), 0.0);
589 }
590 
591 } // namespace operations_research
592 
593 #endif // OR_TOOLS_UTIL_TIME_LIMIT_H_
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
double GetDeterministicTimeLeft() const
Returns the remaining deterministic time before LimitReached() returns true due to the deterministic ...
Definition: time_limit.h:213
void SetInstructionLimit(double instruction_limit)
Sets the instruction limit.
Definition: time_limit.h:172
~NestedTimeLimit()
Updates elapsed deterministic time in the base time limit object.
Definition: time_limit.h:341
double GetElapsedDeterministicTime() const
Definition: time_limit.h:386
std::string DebugString() const
Returns information about the time limit object in a human-readable form.
void MergeWithGlobalTimeLimit(TimeLimit *other)
Definition: time_limit.h:517
static const double kSafetyBufferSeconds
Definition: time_limit.h:108
double GetElapsedTime() const
Returns the time elapsed in seconds since the construction of this object.
Definition: time_limit.h:252
void AdvanceDeterministicTime(double deterministic_duration, const char *counter_name)
Advances the deterministic time.
Definition: time_limit.h:241
double GetInstructionsLeft()
Returns the number of instructions left to reach the limit.
Definition: time_limit.h:587
bool LimitReached() const
Definition: time_limit.h:359
void UpdateLocalLimit(TimeLimit *local_limit)
Definition: time_limit.h:371
static const int kHistorySize
Definition: time_limit.h:109
Definition: cp_model.h:52
~SharedTimeLimit()
Definition: time_limit.h:353
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
Definition: time_limit.h:261
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
Definition: time_limit.h:227
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
TimeLimit * GetTimeLimit()
Returns a time limit object that represents the combination of the overall time limit and the part-sp...
Definition: time_limit.h:463
TimeLimit()
Definition: time_limit.h:127
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:538
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:450
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:576
void Stop()
Definition: time_limit.h:366
double GetTimeLeft() const
Definition: time_limit.h:381
void ResetLimitFromParameters(const Parameters &parameters)
Sets new time limits.
Definition: time_limit.h:511
double ReadInstructionCounter()
Returns the number of instructions executed since the creation of this object.
Definition: time_limit.h:528
std::atomic< bool > * ExternalBooleanAsLimit() const
Returns the current external Boolean limit.
Definition: time_limit.h:283
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:376
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Registers the external Boolean to check when LimitReached() is called.
Definition: time_limit.h:275
SharedTimeLimit(TimeLimit *time_limit)
Definition: time_limit.h:343
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:160
Provides a way to nest time limits for algorithms where a certain part of the computation is bounded ...
Definition: time_limit.h:428
TimeLimit & operator=(const TimeLimit &)=delete
NestedTimeLimit(TimeLimit *base_time_limit, double limit_in_seconds, double deterministic_limit)
Creates the nested time limit.
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:145
friend class ParallelTimeLimit
Definition: time_limit.h:337
DECLARE_bool(time_limit_use_usertime)
Enables changing the behavior of the TimeLimit class to use -b usertime instead of walltime.