22 #include "absl/container/flat_hash_map.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_format.h"
25 #include "absl/time/clock.h"
26 #include "absl/time/time.h"
36 #include "ortools/constraint_solver/demon_profiler.pb.h"
41 Container(
const Constraint* ct_, int64_t value_) :
ct(ct_),
value(value_) {}
42 bool operator<(
const Container& c)
const {
return value > c.value; }
56 active_constraint_(nullptr),
57 active_demon_(nullptr),
58 start_time_ns_(
absl::GetCurrentTimeNanos()) {}
62 constraint_map_.end());
68 return (absl::GetCurrentTimeNanos() - start_time_ns_) / 1000;
77 CHECK(active_constraint_ ==
nullptr);
78 CHECK(active_demon_ ==
nullptr);
79 CHECK(constraint !=
nullptr);
80 ConstraintRuns*
const ct_run =
new ConstraintRuns;
81 ct_run->set_constraint_id(constraint->
DebugString());
82 ct_run->add_initial_propagation_start_time(
CurrentTime());
83 active_constraint_ = constraint;
84 constraint_map_[constraint] = ct_run;
88 CHECK(active_constraint_ !=
nullptr);
89 CHECK(active_demon_ ==
nullptr);
90 CHECK(constraint !=
nullptr);
91 CHECK_EQ(constraint, active_constraint_);
92 ConstraintRuns*
const ct_run = constraint_map_[constraint];
93 if (ct_run !=
nullptr) {
94 ct_run->add_initial_propagation_end_time(
CurrentTime());
95 ct_run->set_failures(0);
97 active_constraint_ =
nullptr;
106 CHECK(active_constraint_ ==
nullptr);
107 CHECK(active_demon_ ==
nullptr);
108 CHECK(constraint !=
nullptr);
109 CHECK(delayed !=
nullptr);
110 ConstraintRuns*
const ct_run = constraint_map_[constraint];
111 ct_run->add_initial_propagation_start_time(
CurrentTime());
112 active_constraint_ = constraint;
117 CHECK(active_constraint_ !=
nullptr);
118 CHECK(active_demon_ ==
nullptr);
119 CHECK(constraint !=
nullptr);
120 CHECK(delayed !=
nullptr);
121 CHECK_EQ(constraint, active_constraint_);
122 ConstraintRuns*
const ct_run = constraint_map_[constraint];
123 if (ct_run !=
nullptr) {
124 ct_run->add_initial_propagation_end_time(
CurrentTime());
125 ct_run->set_failures(0);
127 active_constraint_ =
nullptr;
135 if (demon_map_.find(demon) == demon_map_.end()) {
136 CHECK(active_constraint_ !=
nullptr);
137 CHECK(active_demon_ ==
nullptr);
138 CHECK(demon !=
nullptr);
139 ConstraintRuns*
const ct_run = constraint_map_[active_constraint_];
140 DemonRuns*
const demon_run = ct_run->add_demons();
142 demon_run->set_failures(0);
143 demon_map_[demon] = demon_run;
144 demons_per_constraint_[active_constraint_].push_back(demon_run);
149 CHECK(demon !=
nullptr);
153 CHECK(active_demon_ ==
nullptr);
154 active_demon_ = demon;
155 DemonRuns*
const demon_run = demon_map_[active_demon_];
156 if (demon_run !=
nullptr) {
162 CHECK(demon !=
nullptr);
166 CHECK_EQ(active_demon_, demon);
167 DemonRuns*
const demon_run = demon_map_[active_demon_];
168 if (demon_run !=
nullptr) {
171 active_demon_ =
nullptr;
180 if (active_demon_ !=
nullptr) {
181 DemonRuns*
const demon_run = demon_map_[active_demon_];
182 if (demon_run !=
nullptr) {
184 demon_run->set_failures(demon_run->failures() + 1);
186 active_demon_ =
nullptr;
188 active_constraint_ =
nullptr;
189 }
else if (active_constraint_ !=
nullptr) {
190 ConstraintRuns*
const ct_run = constraint_map_[active_constraint_];
191 if (ct_run !=
nullptr) {
192 ct_run->add_initial_propagation_end_time(
CurrentTime());
193 ct_run->set_failures(1);
195 active_constraint_ =
nullptr;
202 constraint_map_.end());
203 constraint_map_.clear();
205 demons_per_constraint_.clear();
212 int64_t new_max)
override {}
221 const std::vector<int64_t>& values)
override {}
223 const std::vector<int64_t>& values)
override {}
228 int64_t new_max)
override {}
232 int64_t new_max)
override {}
236 int64_t new_max)
override {}
243 const std::vector<int>& rank_last,
244 const std::vector<int>& unperformed)
override {}
249 CHECK(demon !=
nullptr);
250 DemonRuns*
const demon_run = demon_map_[demon];
251 CHECK(demon_run !=
nullptr);
252 demon_run->add_start_time(start_time);
253 demon_run->add_end_time(end_time);
255 demon_run->set_failures(demon_run->failures() + 1);
261 const char*
const kConstraintFormat =
262 " - Constraint: %s\n failures=%d, initial propagation "
263 "runtime=%d us, demons=%d, demon invocations=%d, total demon "
265 const char*
const kDemonFormat =
266 " --- Demon: %s\n invocations=%d, failures=%d, total "
267 "runtime=%d us, [average=%.2lf, median=%.2lf, stddev=%.2lf]\n";
269 const std::string
model =
273 std::vector<Container> to_sort;
275 ConstraintRuns*>::const_iterator it =
276 constraint_map_.begin();
277 it != constraint_map_.end(); ++it) {
280 int64_t demon_invocations = 0;
281 int64_t initial_propagation_runtime = 0;
282 int64_t total_demon_runtime = 0;
285 &demon_invocations, &total_demon_runtime,
288 Container(
ct, total_demon_runtime + initial_propagation_runtime));
290 std::sort(to_sort.begin(), to_sort.end());
292 for (
int i = 0; i < to_sort.size(); ++i) {
295 int64_t demon_invocations = 0;
296 int64_t initial_propagation_runtime = 0;
297 int64_t total_demon_runtime = 0;
300 &demon_invocations, &total_demon_runtime,
302 const std::string constraint_message =
303 absl::StrFormat(kConstraintFormat,
ct->DebugString(), fails,
304 initial_propagation_runtime, demon_count,
305 demon_invocations, total_demon_runtime);
308 const std::vector<DemonRuns*>& demons = demons_per_constraint_[
ct];
309 const int demon_size = demons.size();
310 for (
int demon_index = 0; demon_index < demon_size; ++demon_index) {
311 DemonRuns*
const demon_runs = demons[demon_index];
312 int64_t invocations = 0;
315 double mean_runtime = 0;
316 double median_runtime = 0;
317 double standard_deviation = 0.0;
319 &mean_runtime, &median_runtime,
320 &standard_deviation);
321 const std::string
runs = absl::StrFormat(
322 kDemonFormat, demon_runs->demon_id(), invocations, fails, runtime,
323 mean_runtime, median_runtime, standard_deviation);
333 int64_t*
const fails,
334 int64_t*
const initial_propagation_runtime,
335 int64_t*
const demon_invocations,
336 int64_t*
const total_demon_runtime,
int* demons) {
337 CHECK(constraint !=
nullptr);
338 ConstraintRuns*
const ct_run = constraint_map_[constraint];
339 CHECK(ct_run !=
nullptr);
340 *demon_invocations = 0;
341 *fails = ct_run->failures();
342 *initial_propagation_runtime = 0;
343 for (
int i = 0; i < ct_run->initial_propagation_start_time_size(); ++i) {
344 *initial_propagation_runtime += ct_run->initial_propagation_end_time(i) -
345 ct_run->initial_propagation_start_time(i);
347 *total_demon_runtime = 0;
350 *demons = ct_run->demons_size();
351 CHECK_EQ(*demons, demons_per_constraint_[constraint].size());
352 for (
int demon_index = 0; demon_index < *demons; ++demon_index) {
353 const DemonRuns& demon_runs = ct_run->demons(demon_index);
354 *fails += demon_runs.failures();
355 CHECK_EQ(demon_runs.start_time_size(), demon_runs.end_time_size());
356 const int runs = demon_runs.start_time_size();
357 *demon_invocations +=
runs;
358 for (
int run_index = 0; run_index <
runs; ++run_index) {
359 const int64_t demon_time =
360 demon_runs.end_time(run_index) - demon_runs.start_time(run_index);
361 *total_demon_runtime += demon_time;
367 int64_t*
const demon_invocations, int64_t*
const fails,
368 int64_t*
const total_demon_runtime,
369 double*
const mean_demon_runtime,
370 double*
const median_demon_runtime,
371 double*
const stddev_demon_runtime) {
372 CHECK(demon_runs !=
nullptr);
373 CHECK_EQ(demon_runs->start_time_size(), demon_runs->end_time_size());
375 const int runs = demon_runs->start_time_size();
376 *demon_invocations =
runs;
377 *fails = demon_runs->failures();
378 *total_demon_runtime = 0;
379 *mean_demon_runtime = 0.0;
380 *median_demon_runtime = 0.0;
381 *stddev_demon_runtime = 0.0;
382 std::vector<double> runtimes;
383 for (
int run_index = 0; run_index <
runs; ++run_index) {
384 const int64_t demon_time =
385 demon_runs->end_time(run_index) - demon_runs->start_time(run_index);
386 *total_demon_runtime += demon_time;
387 runtimes.push_back(demon_time);
390 if (!runtimes.empty()) {
391 *mean_demon_runtime = (1.0L * *total_demon_runtime) / runtimes.size();
394 std::sort(runtimes.begin(), runtimes.end());
395 const int pivot = runtimes.size() / 2;
397 if (runtimes.size() == 1) {
398 *median_demon_runtime = runtimes[0];
400 *median_demon_runtime =
401 runtimes.size() % 2 == 1
403 : (runtimes[pivot - 1] + runtimes[pivot]) / 2.0;
407 double total_deviation = 0.0f;
409 for (
int i = 0; i < runtimes.size(); ++i) {
410 total_deviation += pow(runtimes[i] - *mean_demon_runtime, 2);
413 *stddev_demon_runtime = sqrt(total_deviation / runtimes.size());
422 std::string
DebugString()
const override {
return "DemonProfiler"; }
426 Demon* active_demon_;
427 const int64_t start_time_ns_;
428 absl::flat_hash_map<const Constraint*, ConstraintRuns*> constraint_map_;
429 absl::flat_hash_map<const Demon*, DemonRuns*> demon_map_;
430 absl::flat_hash_map<const Constraint*, std::vector<DemonRuns*> >
431 demons_per_constraint_;
435 if (demon_profiler_ !=
nullptr) {
455 CHECK(demon !=
nullptr);
457 propagation_monitor_->RegisterDemon(demon);
470 int64_t start_time, int64_t end_time,
472 monitor->
AddFakeRun(demon, start_time, end_time, is_fail);
477 int64_t*
const fails,
478 int64_t*
const initial_propagation_runtime,
479 int64_t*
const demon_invocations,
480 int64_t*
const total_demon_runtime,
481 int*
const demon_count) {
483 demon_invocations, total_demon_runtime,
A constraint is the main modeling object.
std::string DebugString() const override
A Demon is the base element of a propagation queue.
virtual Solver::DemonPriority priority() const
This method returns the priority of the demon.
std::string DebugString() const override
void BeginFail() override
Just when the failure occurs.
void SetDurationMax(IntervalVar *const var, int64_t new_max) override
void Install() override
Install itself on the solver.
void SetDurationRange(IntervalVar *const var, int64_t new_min, int64_t new_max) override
void SetStartMax(IntervalVar *const var, int64_t new_max) override
void SetMin(IntVar *const var, int64_t new_min) override
IntVar modifiers.
void RestartSearch() override
Restart the search.
void AddFakeRun(Demon *const demon, int64_t start_time, int64_t end_time, bool is_fail)
void SetValue(IntVar *const var, int64_t value) override
void PopContext() override
void SetEndMax(IntervalVar *const var, int64_t new_max) override
void EndProcessingIntegerVariable(IntVar *const var) override
void SetStartMin(IntervalVar *const var, int64_t new_min) override
IntervalVar modifiers.
void SetEndRange(IntervalVar *const var, int64_t new_min, int64_t new_max) override
void SetMin(IntExpr *const expr, int64_t new_min) override
IntExpr modifiers.
void SetPerformed(IntervalVar *const var, bool value) override
void BeginConstraintInitialPropagation(Constraint *const constraint) override
Propagation events.
void SetRange(IntVar *const var, int64_t new_min, int64_t new_max) override
void EndConstraintInitialPropagation(Constraint *const constraint) override
void SetMax(IntVar *const var, int64_t new_max) override
void StartProcessingIntegerVariable(IntVar *const var) override
void RegisterDemon(Demon *const demon) override
void ExportInformation(const DemonRuns *const demon_runs, int64_t *const demon_invocations, int64_t *const fails, int64_t *const total_demon_runtime, double *const mean_demon_runtime, double *const median_demon_runtime, double *const stddev_demon_runtime)
void EndDemonRun(Demon *const demon) override
void SetStartRange(IntervalVar *const var, int64_t new_min, int64_t new_max) override
void RankSequence(SequenceVar *const var, const std::vector< int > &rank_first, const std::vector< int > &rank_last, const std::vector< int > &unperformed) override
void BeginDemonRun(Demon *const demon) override
void SetDurationMin(IntervalVar *const var, int64_t new_min) override
void ExportInformation(const Constraint *const constraint, int64_t *const fails, int64_t *const initial_propagation_runtime, int64_t *const demon_invocations, int64_t *const total_demon_runtime, int *demons)
void RankLast(SequenceVar *const var, int index) override
void PushContext(const std::string &context) override
void EndNestedConstraintInitialPropagation(Constraint *const constraint, Constraint *const delayed) override
void RankNotLast(SequenceVar *const var, int index) override
void RemoveValues(IntVar *const var, const std::vector< int64_t > &values) override
void SetMax(IntExpr *const expr, int64_t new_max) override
void BeginNestedConstraintInitialPropagation(Constraint *const constraint, Constraint *const delayed) override
int64_t CurrentTime() const
void RemoveValue(IntVar *const var, int64_t value) override
DemonProfiler(Solver *const solver)
~DemonProfiler() override
void SetValues(IntVar *const var, const std::vector< int64_t > &values) override
void RankFirst(SequenceVar *const var, int index) override
SequenceVar modifiers.
void SetEndMin(IntervalVar *const var, int64_t new_min) override
void SetRange(IntExpr *const expr, int64_t new_min, int64_t new_max) override
std::string DebugString() const override
void RankNotFirst(SequenceVar *const var, int index) override
void RemoveInterval(IntVar *const var, int64_t imin, int64_t imax) override
void PrintOverview(Solver *const solver, const std::string &filename)
The class IntExpr is the base of all integer expressions in constraint programming.
The class IntVar is a subset of IntExpr.
Interval variables are often used in scheduling.
virtual void Install()
Registers itself on the solver such that it gets notified of the search and propagation events.
A sequence variable is a variable whose domain is a set of possible orderings of the interval variabl...
@ VAR_PRIORITY
VAR_PRIORITY is between DELAYED_PRIORITY and NORMAL_PRIORITY.
@ IN_SEARCH
Executing the search code.
bool IsProfilingEnabled() const
Returns whether we are profiling the solver.
Demon * RegisterDemon(Demon *const demon)
Adds a new demon and wraps it inside a DemonProfiler if necessary.
std::string model_name() const
Returns the name of the model.
void ExportProfilingOverview(const std::string &filename)
Exports the profiling information in a human readable overview.
bool InstrumentsDemons() const
Returns whether we are instrumenting demons.
GurobiMPCallbackContext * context
absl::Status WriteString(File *file, const absl::string_view &contents, int flags)
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
void STLDeleteContainerPairSecondPointers(ForwardIterator begin, ForwardIterator end)
Collection of objects used to extend the Constraint Solver library.
void InstallDemonProfiler(DemonProfiler *const monitor)
void DemonProfilerEndInitialPropagation(DemonProfiler *const monitor, Constraint *const constraint)
void DemonProfilerExportInformation(DemonProfiler *const monitor, const Constraint *const constraint, int64_t *const fails, int64_t *const initial_propagation_runtime, int64_t *const demon_invocations, int64_t *const total_demon_runtime, int *const demon_count)
void RegisterDemon(Solver *const solver, Demon *const demon, DemonProfiler *const monitor)
void DemonProfilerBeginInitialPropagation(DemonProfiler *const monitor, Constraint *const constraint)
DemonProfiler * BuildDemonProfiler(Solver *const solver)
void DeleteDemonProfiler(DemonProfiler *const monitor)
void DemonProfilerAddFakeRun(DemonProfiler *const monitor, Demon *const demon, int64_t start_time, int64_t end_time, bool is_fail)