OR-Tools  9.2
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 std::string DebugString() const;
298
299 private:
300 void ResetTimers(double limit_in_seconds, double deterministic_limit,
301 double instruction_limit);
302
303 std::string GetInstructionRetiredEventName() const {
304 return "inst_retired:any_p:u";
305 }
306
307 mutable int64_t start_ns_; // Not const! this is initialized after
308 // instruction counter initialization.
309 int64_t last_ns_;
310 int64_t limit_ns_; // Not const! See the code of LimitReached().
311 const int64_t safety_buffer_ns_;
312 RunningMax<int64_t> running_max_;
313
314 // Only used when FLAGS_time_limit_use_usertime is true.
315 UserTimer user_timer_;
316 double limit_in_seconds_;
317
318 double deterministic_limit_;
319 double elapsed_deterministic_time_;
320
321 std::atomic<bool>* external_boolean_as_limit_;
322
323#ifdef HAS_PERF_SUBSYSTEM
324 // PMU counter to help count the instructions.
325 exegesis::PerfSubsystem perf_subsystem_;
326#endif // HAS_PERF_SUBSYSTEM
327 // Given limit in terms of number of instructions.
328 double instruction_limit_;
329
330#ifndef NDEBUG
331 // Contains the values of the deterministic time counters.
332 absl::flat_hash_map<std::string, double> deterministic_counters_;
333#endif
334
335 friend class NestedTimeLimit;
336 friend class ParallelTimeLimit;
337};
338
339// Wrapper around TimeLimit to make it thread safe and add Stop() support.
341 public:
343 : time_limit_(time_limit), stopped_boolean_(false) {
344 // We use the one already registered if present or ours otherwise.
345 stopped_ = time_limit->ExternalBooleanAsLimit();
346 if (stopped_ == nullptr) {
347 stopped_ = &stopped_boolean_;
348 time_limit->RegisterExternalBooleanAsLimit(stopped_);
349 }
350 }
351
353 if (stopped_ == &stopped_boolean_) {
354 time_limit_->RegisterExternalBooleanAsLimit(nullptr);
355 }
356 }
357
358 bool LimitReached() const {
359 // Note, time_limit_->LimitReached() is not const, and changes internal
360 // state of time_limit_, hence we need a writer's lock.
361 absl::MutexLock lock(&mutex_);
362 return time_limit_->LimitReached();
363 }
364
365 void Stop() {
366 absl::MutexLock lock(&mutex_);
367 *stopped_ = true;
368 }
369
370 void UpdateLocalLimit(TimeLimit* local_limit) {
371 absl::MutexLock lock(&mutex_);
372 local_limit->MergeWithGlobalTimeLimit(time_limit_);
373 }
374
375 void AdvanceDeterministicTime(double deterministic_duration) {
376 absl::MutexLock lock(&mutex_);
377 time_limit_->AdvanceDeterministicTime(deterministic_duration);
378 }
379
380 double GetTimeLeft() const {
381 absl::ReaderMutexLock lock(&mutex_);
382 return time_limit_->GetTimeLeft();
383 }
384
386 absl::ReaderMutexLock lock(&mutex_);
387 return time_limit_->GetElapsedDeterministicTime();
388 }
389
390 private:
391 mutable absl::Mutex mutex_;
392 TimeLimit* time_limit_ ABSL_GUARDED_BY(mutex_);
393 std::atomic<bool> stopped_boolean_ ABSL_GUARDED_BY(mutex_);
394 std::atomic<bool>* stopped_ ABSL_GUARDED_BY(mutex_);
395};
396
428 public:
433 NestedTimeLimit(TimeLimit* base_time_limit, double limit_in_seconds,
434 double deterministic_limit);
435
440
448 template <typename Parameters>
449 static std::unique_ptr<NestedTimeLimit> FromBaseTimeLimitAndParameters(
450 TimeLimit* time_limit, const Parameters& parameters) {
451 return absl::make_unique<NestedTimeLimit>(
452 time_limit, parameters.max_time_in_seconds(),
453 parameters.max_deterministic_time());
454 }
455
462 TimeLimit* GetTimeLimit() { return &time_limit_; }
463
464 private:
465 TimeLimit* const base_time_limit_;
466 TimeLimit time_limit_;
467
468 DISALLOW_COPY_AND_ASSIGN(NestedTimeLimit);
469};
470
471// ################## Implementations below #####################
472
473inline TimeLimit::TimeLimit(double limit_in_seconds, double deterministic_limit,
474 double instruction_limit)
475 : safety_buffer_ns_(static_cast<int64_t>(kSafetyBufferSeconds * 1e9)),
476 running_max_(kHistorySize),
477 external_boolean_as_limit_(nullptr) {
478 ResetTimers(limit_in_seconds, deterministic_limit, instruction_limit);
479}
480
481inline void TimeLimit::ResetTimers(double limit_in_seconds,
482 double deterministic_limit,
483 double instruction_limit) {
484 elapsed_deterministic_time_ = 0.0;
485 deterministic_limit_ = deterministic_limit;
486 instruction_limit_ = instruction_limit;
487
488 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
489 user_timer_.Start();
490 limit_in_seconds_ = limit_in_seconds;
491 }
492#ifdef HAS_PERF_SUBSYSTEM
493 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
494 perf_subsystem_.CleanUp();
495 perf_subsystem_.AddEvent(GetInstructionRetiredEventName());
496 perf_subsystem_.StartCollecting();
497 }
498#endif // HAS_PERF_SUBSYSTEM
499 start_ns_ = absl::GetCurrentTimeNanos();
500 last_ns_ = start_ns_;
501 limit_ns_ = limit_in_seconds >= 1e-9 * (kint64max - start_ns_)
502 ? kint64max
503 : static_cast<int64_t>(limit_in_seconds * 1e9) + start_ns_;
504}
505
506template <typename Parameters>
507inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) {
508 ResetTimers(parameters.max_time_in_seconds(),
509 parameters.max_deterministic_time(),
510 std::numeric_limits<double>::infinity());
511}
512
514 if (other == nullptr) return;
515 ResetTimers(
516 std::min(GetTimeLeft(), other->GetTimeLeft()),
518 std::numeric_limits<double>::infinity());
519 if (other->ExternalBooleanAsLimit() != nullptr) {
521 }
522}
523
525#ifdef HAS_PERF_SUBSYSTEM
526 if (absl::GetFlag(FLAGS_time_limit_use_instruction_count)) {
527 return perf_subsystem_.ReadCounters().GetScaledOrDie(
528 GetInstructionRetiredEventName());
529 }
530#endif // HAS_PERF_SUBSYSTEM
531 return 0;
532}
533
535 if (external_boolean_as_limit_ != nullptr &&
536 external_boolean_as_limit_->load()) {
537 return true;
538 }
539
540 if (GetDeterministicTimeLeft() <= 0.0) {
541 return true;
542 }
543
544#ifdef HAS_PERF_SUBSYSTEM
545 if (ReadInstructionCounter() >= instruction_limit_) {
546 return true;
547 }
548#endif // HAS_PERF_SUBSYSTEM
549
550 const int64_t current_ns = absl::GetCurrentTimeNanos();
551 running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_));
552 last_ns_ = current_ns;
553 if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) {
554 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
555 // To avoid making many system calls, we only check the user time when
556 // the "absolute" time limit has been reached. Note that the user time
557 // should advance more slowly, so this is correct.
558 const double time_left_s = limit_in_seconds_ - user_timer_.Get();
559 if (time_left_s > kSafetyBufferSeconds) {
560 limit_ns_ = static_cast<int64_t>(time_left_s * 1e9) + last_ns_;
561 return false;
562 }
563 }
564
565 // To ensure that future calls to LimitReached() will return true.
566 limit_ns_ = 0;
567 return true;
568 }
569 return false;
570}
571
572inline double TimeLimit::GetTimeLeft() const {
573 if (limit_ns_ == kint64max) return std::numeric_limits<double>::infinity();
574 const int64_t delta_ns = limit_ns_ - absl::GetCurrentTimeNanos();
575 if (delta_ns < 0) return 0.0;
576 if (absl::GetFlag(FLAGS_time_limit_use_usertime)) {
577 return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);
578 } else {
579 return delta_ns * 1e-9;
580 }
581}
582
584 return std::max(instruction_limit_ - ReadInstructionCounter(), 0.0);
585}
586
587} // namespace operations_research
588
589#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:892
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:427
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:449
TimeLimit * GetTimeLimit()
Returns a time limit object that represents the combination of the overall time limit and the part-sp...
Definition: time_limit.h:462
~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:370
SharedTimeLimit(TimeLimit *time_limit)
Definition: time_limit.h:342
double GetElapsedDeterministicTime() const
Definition: time_limit.h:385
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:375
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:583
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:507
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:572
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:524
void RegisterExternalBooleanAsLimit(std::atomic< bool > *external_boolean_as_limit)
Registers the external Boolean to check when LimitReached() is called.
Definition: time_limit.h:274
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:534
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:513
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 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.