OR-Tools  9.1
time_limit.h
Go to the documentation of this file.
1// Copyright 2010-2021 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"
30#include "ortools/base/macros.h"
31#include "ortools/base/timer.h"
33#ifdef HAS_PERF_SUBSYSTEM
34#include "exegesis/exegesis/itineraries/perf_subsystem.h"
35#endif // HAS_PERF_SUBSYSTEM
36
41ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
42
47ABSL_DECLARE_FLAG(bool, time_limit_use_instruction_count);
48
49namespace operations_research {
50
103// TODO(user): The expression "deterministic time" should be replaced with
104// "number of operations" to avoid confusion with "real" time.
106 public:
107 static const double kSafetyBufferSeconds; // See the .cc for the value.
108 static const int kHistorySize;
109
121 explicit TimeLimit(
122 double limit_in_seconds,
123 double deterministic_limit = std::numeric_limits<double>::infinity(),
124 double instruction_limit = std::numeric_limits<double>::infinity());
125
126 TimeLimit() : TimeLimit(std::numeric_limits<double>::infinity()) {}
127 TimeLimit(const TimeLimit&) = delete;
128 TimeLimit& operator=(const TimeLimit&) = delete;
129
134 static std::unique_ptr<TimeLimit> Infinite() {
135 return absl::make_unique<TimeLimit>(
136 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 absl::make_unique<TimeLimit>(
147 std::numeric_limits<double>::infinity(), 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 absl::make_unique<TimeLimit>(
162 parameters.max_time_in_seconds(), 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
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);
292
296 std::string DebugString() const;
297
298 private:
299 void ResetTimers(double limit_in_seconds, double deterministic_limit,
300 double instruction_limit);
301
302 std::string GetInstructionRetiredEventName() const {
303 return "inst_retired:any_p:u";
304 }
305
306 mutable int64_t start_ns_; // Not const! this is initialized after
307 // instruction counter initialization.
308 int64_t last_ns_;
309 int64_t limit_ns_; // Not const! See the code of LimitReached().
310 const int64_t safety_buffer_ns_;
311 RunningMax<int64_t> running_max_;
312
313 // Only used when FLAGS_time_limit_use_usertime is true.
314 UserTimer user_timer_;
315 double limit_in_seconds_;
316
317 double deterministic_limit_;
318 double elapsed_deterministic_time_;
319
320 std::atomic<bool>* external_boolean_as_limit_;
321
322#ifdef HAS_PERF_SUBSYSTEM
323 // PMU counter to help count the instructions.
324 exegesis::PerfSubsystem perf_subsystem_;
325#endif // HAS_PERF_SUBSYSTEM
326 // Given limit in terms of number of instructions.
327 double instruction_limit_;
328
329#ifndef NDEBUG
330 // Contains the values of the deterministic time counters.
331 absl::flat_hash_map<std::string, double> deterministic_counters_;
332#endif
333
334 friend class NestedTimeLimit;
335 friend class ParallelTimeLimit;
336};
337
338// Wrapper around TimeLimit to make it thread safe and add Stop() support.
340 public:
342 : time_limit_(time_limit), stopped_boolean_(false) {
343 // We use the one already registered if present or ours otherwise.
344 stopped_ = time_limit->ExternalBooleanAsLimit();
345 if (stopped_ == nullptr) {
346 stopped_ = &stopped_boolean_;
347 time_limit->RegisterExternalBooleanAsLimit(stopped_);
348 }
349 }
350
352 if (stopped_ == &stopped_boolean_) {
353 time_limit_->RegisterExternalBooleanAsLimit(nullptr);
354 }
355 }
356
357 bool LimitReached() const {
358 // Note, time_limit_->LimitReached() is not const, and changes internal
359 // state of time_limit_, hence we need a writer's lock.
360 absl::MutexLock lock(&mutex_);
361 return time_limit_->LimitReached();
362 }
363
364 void Stop() {
365 absl::MutexLock lock(&mutex_);
366 *stopped_ = true;
367 }
368
369 void UpdateLocalLimit(TimeLimit* local_limit) {
370 absl::MutexLock lock(&mutex_);
371 local_limit->MergeWithGlobalTimeLimit(time_limit_);
372 }
373
374 void AdvanceDeterministicTime(double deterministic_duration) {
375 absl::MutexLock lock(&mutex_);
376 time_limit_->AdvanceDeterministicTime(deterministic_duration);
377 }
378
379 double GetTimeLeft() const {
380 absl::ReaderMutexLock lock(&mutex_);
381 return time_limit_->GetTimeLeft();
382 }
383
385 absl::ReaderMutexLock lock(&mutex_);
386 return time_limit_->GetElapsedDeterministicTime();
387 }
388
389 private:
390 mutable absl::Mutex mutex_;
391 TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
392 std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
393 std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
394};
395
427 public:
432 NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
433 double deterministic_limit);
434
439
447 template <typename Parameters>
448 static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
449 TimeLimit* time_limit, const Parameters& parameters) {
450 return absl::make_unique<NestedTimeLimit>(
451 time_limit, parameters.max_time_in_seconds(),
452 parameters.max_deterministic_time());
453 }
454
461 TimeLimit* GetTimeLimit() { return &time_limit_; }
462
463 private:
464 TimeLimit* const base_time_limit_;
465 TimeLimit time_limit_;
466
467 DISALLOW_COPY_AND_ASSIGN(NestedTimeLimit);
468};
469
470// ################## Implementations below #####################
471
472inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit,
473 double instruction_limit)
474 : safety_buffer_ns_(static_cast<int64_t>(kSafetyBufferSeconds * 1e9)),
475 running_max_(kHistorySize),
476 external_boolean_as_limit_(nullptr) {
477 ResetTimers(limit_in_seconds, deterministic_limit, instruction_limit);
478}
479
480inline void TimeLimit::ResetTimers(double limit_in_seconds,
481 double deterministic_limit,
482 double instruction_limit) {
483 elapsed_deterministic_time_ = 0.0;
484 deterministic_limit_ = deterministic_limit;
485 instruction_limit_ = instruction_limit;
486
487 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
488 user_timer_.Start();
489 limit_in_seconds_ = limit_in_seconds;
490 }
491#ifdef HAS_PERF_SUBSYSTEM
492 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
493 perf_subsystem_.CleanUp();
494 perf_subsystem_.AddEvent(GetInstructionRetiredEventName());
495 perf_subsystem_.StartCollecting();
496 }
497#endif // HAS_PERF_SUBSYSTEM
498 start_ns_ = absl::GetCurrentTimeNanos();
499 last_ns_ = start_ns_;
500 limit_ns_ = limit_in_seconds >= 1e-9 * (kint64max - start_ns_)
501 ? kint64max
502 : static_cast<int64_t>(limit_in_seconds * 1e9) + start_ns_;
503}
504
505template <typename Parameters>
506inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
507 ResetTimers(parameters.max_time_in_seconds(),
508 parameters.max_deterministic_time(),
509 std::numeric_limits<double>::infinity());
510}
511
513 if (other == nullptr) return;
514 ResetTimers(
515 std::min(GetTimeLeft(), other->GetTimeLeft()),
517 std::numeric_limits<double>::infinity());
518 if (other->ExternalBooleanAsLimit() != nullptr) {
520 }
521}
522
524#ifdef HAS_PERF_SUBSYSTEM
525 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
526 return perf_subsystem_.ReadCounters().GetScaledOrDie(
527 GetInstructionRetiredEventName());
528 }
529#endif // HAS_PERF_SUBSYSTEM
530 return 0;
531}
532
534 if (external_boolean_as_limit_ != nullptr &&
535 external_boolean_as_limit_->load()) {
536 return true;
537 }
538
539 if (GetDeterministicTimeLeft() <= 0.0) {
540 return true;
541 }
542
543#ifdef HAS_PERF_SUBSYSTEM
544 if (ReadInstructionCounter() >= instruction_limit_) {
545 return true;
546 }
547#endif // HAS_PERF_SUBSYSTEM
548
549 const int64_t current_ns = absl::GetCurrentTimeNanos();
550 running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
551 last_ns_ = current_ns;
552 if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
553 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
554 // To avoid making many system calls, we only check the user time when
555 // the "absolute" time limit has been reached. Note that the user time
556 // should advance more slowly, so this is correct.
557 const double time_left_s = limit_in_seconds_ - user_timer_.Get();
558 if (time_left_s > kSafetyBufferSeconds) {
559 limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
560 return false;
561 }
562 }
563
564 // To ensure that future calls to LimitReached() will return true.
565 limit_ns_ = 0;
566 return true;
567 }
568 return false;
569}
570
571inline double TimeLimit::GetTimeLeft() const {
572 if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
573 const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
574 if (delta_ns < 0) return 0.0;
575 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
576 return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
577 } else {
578 return delta_ns * 1e-9;
579 }
580}
581
583 return std::max(instruction_limit_ - ReadInstructionCounter(), 0.0);
584}
585
586} // namespace operations_research
587
588#endif // OR_TOOLS_UTIL_TIME_LIMIT_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
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:426
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:448
TimeLimit * GetTimeLimit()
Returns a time limit object that represents the combination of the overall time limit and the part-sp...
Definition: time_limit.h:461
~NestedTimeLimit()
Updates elapsed deterministic time in the base time limit object.
Definition: time_limit.cc:60
NestedTimeLimit(TimeLimit *base_time_limit, double limit_in_seconds, double deterministic_limit)
Creates the nested time limit.
Definition: time_limit.cc:47
void UpdateLocalLimit(TimeLimit *local_limit)
Definition: time_limit.h:369
SharedTimeLimit(TimeLimit *time_limit)
Definition: time_limit.h:341
double GetElapsedDeterministicTime() const
Definition: time_limit.h:384
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:374
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
double GetInstructionsLeft()
Returns the number of instructions left to reach the limit.
Definition: time_limit.h:582
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
static const double kSafetyBufferSeconds
Definition: time_limit.h:107
void ResetLimitFromParameters(const Parameters &parameters)
Sets new time limits.
Definition: time_limit.h:506
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:571
void SetInstructionLimit(double instruction_limit)
Sets the instruction limit.
Definition: time_limit.h:171
std::atomic< bool > * ExternalBooleanAsLimit() const
Returns the current external Boolean limit.
Definition: time_limit.h:281
double ReadInstructionCounter()
Returns the number of instructions executed since the creation of this object.
Definition: time_limit.h:523
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Registers the external Boolean to check when LimitReached() is called.
Definition: time_limit.h:273
std::string DebugString() const
Returns information about the time limit object in a human-readable form.
Definition: time_limit.cc:31
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:533
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:134
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
TimeLimit(const TimeLimit &)=delete
static const int kHistorySize
Definition: time_limit.h:108
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:512
TimeLimit & operator=(const TimeLimit &)=delete
double GetElapsedTime() const
Returns the time elapsed in seconds since the construction of this object.
Definition: time_limit.h:251
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.
STL namespace.
ABSL_DECLARE_FLAG(bool, time_limit_use_usertime)
Enables changing the behavior of the TimeLimit class to use -b usertime instead of walltime.