OR-Tools  9.3
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/base/port.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/memory/memory.h"
27#include "absl/synchronization/mutex.h"
28#include "absl/time/clock.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
42ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
43
48ABSL_DECLARE_FLAG(bool, time_limit_use_instruction_count);
49
50namespace operations_research {
51
104// TODO(user): The expression "deterministic time" should be replaced with
105// "number of operations" to avoid confusion with "real" time.
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
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
275 std::atomic<bool>* external_boolean_as_limit) {
276 external_boolean_as_limit_ = external_boolean_as_limit;
277 }
278
282 std::atomic<bool>* ExternalBooleanAsLimit() const {
283 return external_boolean_as_limit_;
284 }
285
290 template <typename Parameters>
291 void ResetLimitFromParameters(const Parameters& parameters);
293
297 void ChangeDeterministicLimit(double new_limit) {
298 deterministic_limit_ = new_limit;
299 }
300
304 double GetDeterministicLimit() const { return deterministic_limit_; }
305
309 std::string DebugString() const;
310
311 private:
312 void ResetTimers(double limit_in_seconds, double deterministic_limit,
313 double instruction_limit);
314
315 std::string GetInstructionRetiredEventName() const {
316 return "inst_retired:any_p:u";
317 }
318
319 mutable int64_t start_ns_; // Not const! this is initialized after
320 // instruction counter initialization.
321 int64_t last_ns_;
322 int64_t limit_ns_; // Not const! See the code of LimitReached().
323 const int64_t safety_buffer_ns_;
324 RunningMax<int64_t> running_max_;
325
326 // Only used when FLAGS_time_limit_use_usertime is true.
327 UserTimer user_timer_;
328 double limit_in_seconds_;
329
330 double deterministic_limit_;
331 double elapsed_deterministic_time_;
332
333 std::atomic<bool>* external_boolean_as_limit_;
334
335#ifdef HAS_PERF_SUBSYSTEM
336 // PMU counter to help count the instructions.
337 exegesis::PerfSubsystem perf_subsystem_;
338#endif // HAS_PERF_SUBSYSTEM
339 // Given limit in terms of number of instructions.
340 double instruction_limit_;
341
342#ifndef NDEBUG
343 // Contains the values of the deterministic time counters.
344 absl::flat_hash_map<std::string, double> deterministic_counters_;
345#endif
346
347 friend class NestedTimeLimit;
348 friend class ParallelTimeLimit;
349};
350
351// Wrapper around TimeLimit to make it thread safe and add Stop() support.
353 public:
355 : time_limit_(time_limit), stopped_boolean_(false) {
356 // We use the one already registered if present or ours otherwise.
357 stopped_ = time_limit->ExternalBooleanAsLimit();
358 if (stopped_ == nullptr) {
359 stopped_ = &stopped_boolean_;
360 time_limit->RegisterExternalBooleanAsLimit(stopped_);
361 }
362 }
363
365 if (stopped_ == &stopped_boolean_) {
366 time_limit_->RegisterExternalBooleanAsLimit(nullptr);
367 }
368 }
369
370 bool LimitReached() const {
371 // Note, time_limit_->LimitReached() is not const, and changes internal
372 // state of time_limit_, hence we need a writer's lock.
373 absl::MutexLock lock(&mutex_);
374 return time_limit_->LimitReached();
375 }
376
377 void Stop() {
378 absl::MutexLock lock(&mutex_);
379 *stopped_ = true;
380 }
381
382 void UpdateLocalLimit(TimeLimit* local_limit) {
383 absl::MutexLock lock(&mutex_);
384 local_limit->MergeWithGlobalTimeLimit(time_limit_);
385 }
386
387 void AdvanceDeterministicTime(double deterministic_duration) {
388 absl::MutexLock lock(&mutex_);
389 time_limit_->AdvanceDeterministicTime(deterministic_duration);
390 }
391
392 double GetTimeLeft() const {
393 absl::ReaderMutexLock lock(&mutex_);
394 return time_limit_->GetTimeLeft();
395 }
396
398 absl::ReaderMutexLock lock(&mutex_);
399 return time_limit_->GetElapsedDeterministicTime();
400 }
401
402 private:
403 mutable absl::Mutex mutex_;
404 TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
405 std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
406 std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
407};
408
440 public:
445 NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
446 double deterministic_limit);
447
452
460 template <typename Parameters>
461 static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
462 TimeLimit* time_limit, const Parameters& parameters) {
463 return absl::make_unique<NestedTimeLimit>(
464 time_limit, parameters.max_time_in_seconds(),
465 parameters.max_deterministic_time());
466 }
467
474 TimeLimit* GetTimeLimit() { return &time_limit_; }
475
476 private:
477 TimeLimit* const base_time_limit_;
478 TimeLimit time_limit_;
479
480 DISALLOW_COPY_AND_ASSIGN(NestedTimeLimit);
481};
482
483// ################## Implementations below #####################
484
485inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit,
486 double instruction_limit)
487 : safety_buffer_ns_(static_cast<int64_t>(kSafetyBufferSeconds * 1e9)),
488 running_max_(kHistorySize),
489 external_boolean_as_limit_(nullptr) {
490 ResetTimers(limit_in_seconds, deterministic_limit, instruction_limit);
491}
492
493inline void TimeLimit::ResetTimers(double limit_in_seconds,
494 double deterministic_limit,
495 double instruction_limit) {
496 elapsed_deterministic_time_ = 0.0;
497 deterministic_limit_ = deterministic_limit;
498 instruction_limit_ = instruction_limit;
499
500 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
501 user_timer_.Start();
502 limit_in_seconds_ = limit_in_seconds;
503 }
504#ifdef HAS_PERF_SUBSYSTEM
505 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
506 perf_subsystem_.CleanUp();
507 perf_subsystem_.AddEvent(GetInstructionRetiredEventName());
508 perf_subsystem_.StartCollecting();
509 }
510#endif // HAS_PERF_SUBSYSTEM
511 start_ns_ = absl::GetCurrentTimeNanos();
512 last_ns_ = start_ns_;
513 limit_ns_ = limit_in_seconds >= 1e-9 * (kint64max - start_ns_)
514 ? kint64max
515 : static_cast<int64_t>(limit_in_seconds * 1e9) + start_ns_;
516}
517
518template <typename Parameters>
519inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
520 ResetTimers(parameters.max_time_in_seconds(),
521 parameters.max_deterministic_time(),
522 std::numeric_limits<double>::infinity());
523}
524
526 if (other == nullptr) return;
527 ResetTimers(
528 std::min(GetTimeLeft(), other->GetTimeLeft()),
530 std::numeric_limits<double>::infinity());
531 if (other->ExternalBooleanAsLimit() != nullptr) {
533 }
534}
535
537#ifdef HAS_PERF_SUBSYSTEM
538 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
539 return perf_subsystem_.ReadCounters().GetScaledOrDie(
540 GetInstructionRetiredEventName());
541 }
542#endif // HAS_PERF_SUBSYSTEM
543 return 0;
544}
545
547 if (external_boolean_as_limit_ != nullptr &&
548 external_boolean_as_limit_->load()) {
549 return true;
550 }
551
552 if (GetDeterministicTimeLeft() <= 0.0) {
553 return true;
554 }
555
556#ifdef HAS_PERF_SUBSYSTEM
557 if (ReadInstructionCounter() >= instruction_limit_) {
558 return true;
559 }
560#endif // HAS_PERF_SUBSYSTEM
561
562 const int64_t current_ns = absl::GetCurrentTimeNanos();
563 running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
564 last_ns_ = current_ns;
565 if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
566 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
567 // To avoid making many system calls, we only check the user time when
568 // the "absolute" time limit has been reached. Note that the user time
569 // should advance more slowly, so this is correct.
570 const double time_left_s = limit_in_seconds_ - user_timer_.Get();
571 if (time_left_s > kSafetyBufferSeconds) {
572 limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
573 return false;
574 }
575 }
576
577 // To ensure that future calls to LimitReached() will return true.
578 limit_ns_ = 0;
579 return true;
580 }
581 return false;
582}
583
584inline double TimeLimit::GetTimeLeft() const {
585 if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
586 const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
587 if (delta_ns < 0) return 0.0;
588 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
589 return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
590 } else {
591 return delta_ns * 1e-9;
592 }
593}
594
596 return std::max(instruction_limit_ - ReadInstructionCounter(), 0.0);
597}
598
599} // namespace operations_research
600
601#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:893
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:439
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:461
TimeLimit * GetTimeLimit()
Returns a time limit object that represents the combination of the overall time limit and the part-sp...
Definition: time_limit.h:474
~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:382
SharedTimeLimit(TimeLimit *time_limit)
Definition: time_limit.h:354
double GetElapsedDeterministicTime() const
Definition: time_limit.h:397
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:387
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:595
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
static const double kSafetyBufferSeconds
Definition: time_limit.h:108
void ResetLimitFromParameters(const Parameters &parameters)
Sets new time limits.
Definition: time_limit.h:519
double GetDeterministicTimeLeft() const
Returns the remaining deterministic time before LimitReached() returns true due to the deterministic ...
Definition: time_limit.h:213
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:584
void SetInstructionLimit(double instruction_limit)
Sets the instruction limit.
Definition: time_limit.h:172
std::atomic< bool > * ExternalBooleanAsLimit() const
Returns the current external Boolean limit.
Definition: time_limit.h:282
double ReadInstructionCounter()
Returns the number of instructions executed since the creation of this object.
Definition: time_limit.h:536
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Registers the external Boolean to check when LimitReached() is called.
Definition: time_limit.h:274
double GetDeterministicLimit() const
Queries the deterministic time limit.
Definition: time_limit.h:304
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:546
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
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
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:261
void AdvanceDeterministicTime(double deterministic_duration, const char *counter_name)
Advances the deterministic time.
Definition: time_limit.h:241
void MergeWithGlobalTimeLimit(TimeLimit *other)
Definition: time_limit.h:525
TimeLimit & operator=(const TimeLimit &)=delete
double GetElapsedTime() const
Returns the time elapsed in seconds since the construction of this object.
Definition: time_limit.h:252
void ChangeDeterministicLimit(double new_limit)
Overwrites the deterministic time limit with the new value.
Definition: time_limit.h:297
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
Definition: time_limit.h:227
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.