OR-Tools  9.6
demon_profiler.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 #include <algorithm>
15 #include <cmath>
16 #include <cstddef>
17 #include <cstdint>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
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"
27 #include "ortools/base/file.h"
28 #include "ortools/base/hash.h"
29 #include "ortools/base/helpers.h"
31 #include "ortools/base/logging.h"
32 #include "ortools/base/mathutil.h"
33 #include "ortools/base/stl_util.h"
36 #include "ortools/constraint_solver/demon_profiler.pb.h"
37 
38 namespace operations_research {
39 namespace {
40 struct Container {
41  Container(const Constraint* ct_, int64_t value_) : ct(ct_), value(value_) {}
42  bool operator<(const Container& c) const { return value > c.value; }
43 
44  const Constraint* ct;
45  int64_t value;
46 };
47 } // namespace
48 
49 // DemonProfiler manages the profiling of demons and allows access to gathered
50 // data. Add this class as a parameter to Solver and access its information
51 // after the end of a search.
53  public:
54  explicit DemonProfiler(Solver* const solver)
56  active_constraint_(nullptr),
57  active_demon_(nullptr),
58  start_time_ns_(absl::GetCurrentTimeNanos()) {}
59 
60  ~DemonProfiler() override {
61  gtl::STLDeleteContainerPairSecondPointers(constraint_map_.begin(),
62  constraint_map_.end());
63  }
64 
65  // In microseconds.
66  // TODO(user): rename and return nanoseconds.
67  int64_t CurrentTime() const {
68  return (absl::GetCurrentTimeNanos() - start_time_ns_) / 1000;
69  }
70 
72  Constraint* const constraint) override {
73  if (solver()->state() == Solver::IN_SEARCH) {
74  return;
75  }
76 
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;
85  }
86 
87  void EndConstraintInitialPropagation(Constraint* const constraint) override {
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);
96  }
97  active_constraint_ = nullptr;
98  }
99 
101  Constraint* const constraint, Constraint* const delayed) override {
102  if (solver()->state() == Solver::IN_SEARCH) {
103  return;
104  }
105 
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;
113  }
114 
116  Constraint* const constraint, Constraint* const delayed) override {
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);
126  }
127  active_constraint_ = nullptr;
128  }
129 
130  void RegisterDemon(Demon* const demon) override {
131  if (solver()->state() == Solver::IN_SEARCH) {
132  return;
133  }
134 
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();
141  demon_run->set_demon_id(demon->DebugString());
142  demon_run->set_failures(0);
143  demon_map_[demon] = demon_run;
144  demons_per_constraint_[active_constraint_].push_back(demon_run);
145  }
146  }
147 
148  void BeginDemonRun(Demon* const demon) override {
149  CHECK(demon != nullptr);
150  if (demon->priority() == Solver::VAR_PRIORITY) {
151  return;
152  }
153  CHECK(active_demon_ == nullptr);
154  active_demon_ = demon;
155  DemonRuns* const demon_run = demon_map_[active_demon_];
156  if (demon_run != nullptr) {
157  demon_run->add_start_time(CurrentTime());
158  }
159  }
160 
161  void EndDemonRun(Demon* const demon) override {
162  CHECK(demon != nullptr);
163  if (demon->priority() == Solver::VAR_PRIORITY) {
164  return;
165  }
166  CHECK_EQ(active_demon_, demon);
167  DemonRuns* const demon_run = demon_map_[active_demon_];
168  if (demon_run != nullptr) {
169  demon_run->add_end_time(CurrentTime());
170  }
171  active_demon_ = nullptr;
172  }
173 
174  void StartProcessingIntegerVariable(IntVar* const var) override {}
175  void EndProcessingIntegerVariable(IntVar* const var) override {}
176  void PushContext(const std::string& context) override {}
177  void PopContext() override {}
178 
179  void BeginFail() override {
180  if (active_demon_ != nullptr) {
181  DemonRuns* const demon_run = demon_map_[active_demon_];
182  if (demon_run != nullptr) {
183  demon_run->add_end_time(CurrentTime());
184  demon_run->set_failures(demon_run->failures() + 1);
185  }
186  active_demon_ = nullptr;
187  // active_constraint_ can be non null in case of initial propagation.
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);
194  }
195  active_constraint_ = nullptr;
196  }
197  }
198 
199  // Restarts a search and clears all previously collected information.
200  void RestartSearch() override {
201  gtl::STLDeleteContainerPairSecondPointers(constraint_map_.begin(),
202  constraint_map_.end());
203  constraint_map_.clear();
204  demon_map_.clear();
205  demons_per_constraint_.clear();
206  }
207 
208  // IntExpr modifiers.
209  void SetMin(IntExpr* const expr, int64_t new_min) override {}
210  void SetMax(IntExpr* const expr, int64_t new_max) override {}
211  void SetRange(IntExpr* const expr, int64_t new_min,
212  int64_t new_max) override {}
213  // IntVar modifiers.
214  void SetMin(IntVar* const var, int64_t new_min) override {}
215  void SetMax(IntVar* const var, int64_t new_max) override {}
216  void SetRange(IntVar* const var, int64_t new_min, int64_t new_max) override {}
217  void RemoveValue(IntVar* const var, int64_t value) override {}
218  void SetValue(IntVar* const var, int64_t value) override {}
219  void RemoveInterval(IntVar* const var, int64_t imin, int64_t imax) override {}
220  void SetValues(IntVar* const var,
221  const std::vector<int64_t>& values) override {}
222  void RemoveValues(IntVar* const var,
223  const std::vector<int64_t>& values) override {}
224  // IntervalVar modifiers.
225  void SetStartMin(IntervalVar* const var, int64_t new_min) override {}
226  void SetStartMax(IntervalVar* const var, int64_t new_max) override {}
227  void SetStartRange(IntervalVar* const var, int64_t new_min,
228  int64_t new_max) override {}
229  void SetEndMin(IntervalVar* const var, int64_t new_min) override {}
230  void SetEndMax(IntervalVar* const var, int64_t new_max) override {}
231  void SetEndRange(IntervalVar* const var, int64_t new_min,
232  int64_t new_max) override {}
233  void SetDurationMin(IntervalVar* const var, int64_t new_min) override {}
234  void SetDurationMax(IntervalVar* const var, int64_t new_max) override {}
235  void SetDurationRange(IntervalVar* const var, int64_t new_min,
236  int64_t new_max) override {}
237  void SetPerformed(IntervalVar* const var, bool value) override {}
238  void RankFirst(SequenceVar* const var, int index) override {}
239  void RankNotFirst(SequenceVar* const var, int index) override {}
240  void RankLast(SequenceVar* const var, int index) override {}
241  void RankNotLast(SequenceVar* const var, int index) override {}
242  void RankSequence(SequenceVar* const var, const std::vector<int>& rank_first,
243  const std::vector<int>& rank_last,
244  const std::vector<int>& unperformed) override {}
245 
246  // Useful for unit tests.
247  void AddFakeRun(Demon* const demon, int64_t start_time, int64_t end_time,
248  bool is_fail) {
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);
254  if (is_fail) {
255  demon_run->set_failures(demon_run->failures() + 1);
256  }
257  }
258 
259  // Exports collected data as human-readable text.
260  void PrintOverview(Solver* const solver, const std::string& filename) {
261  const char* const kConstraintFormat =
262  " - Constraint: %s\n failures=%d, initial propagation "
263  "runtime=%d us, demons=%d, demon invocations=%d, total demon "
264  "runtime=%d us\n";
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";
268  File* file;
269  const std::string model =
270  absl::StrFormat("Model %s:\n", solver->model_name());
271  if (file::Open(filename, "w", &file, file::Defaults()).ok()) {
272  file::WriteString(file, model, file::Defaults()).IgnoreError();
273  std::vector<Container> to_sort;
274  for (absl::flat_hash_map<const Constraint*,
275  ConstraintRuns*>::const_iterator it =
276  constraint_map_.begin();
277  it != constraint_map_.end(); ++it) {
278  const Constraint* const ct = it->first;
279  int64_t fails = 0;
280  int64_t demon_invocations = 0;
281  int64_t initial_propagation_runtime = 0;
282  int64_t total_demon_runtime = 0;
283  int demon_count = 0;
284  ExportInformation(ct, &fails, &initial_propagation_runtime,
285  &demon_invocations, &total_demon_runtime,
286  &demon_count);
287  to_sort.push_back(
288  Container(ct, total_demon_runtime + initial_propagation_runtime));
289  }
290  std::sort(to_sort.begin(), to_sort.end());
291 
292  for (int i = 0; i < to_sort.size(); ++i) {
293  const Constraint* const ct = to_sort[i].ct;
294  int64_t fails = 0;
295  int64_t demon_invocations = 0;
296  int64_t initial_propagation_runtime = 0;
297  int64_t total_demon_runtime = 0;
298  int demon_count = 0;
299  ExportInformation(ct, &fails, &initial_propagation_runtime,
300  &demon_invocations, &total_demon_runtime,
301  &demon_count);
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);
306  file::WriteString(file, constraint_message, file::Defaults())
307  .IgnoreError();
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;
313  int64_t fails = 0;
314  int64_t runtime = 0;
315  double mean_runtime = 0;
316  double median_runtime = 0;
317  double standard_deviation = 0.0;
318  ExportInformation(demon_runs, &invocations, &fails, &runtime,
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);
324  file::WriteString(file, runs, file::Defaults()).IgnoreError();
325  }
326  }
327  }
328  file->Close(file::Defaults()).IgnoreError();
329  }
330 
331  // Export Information
332  void ExportInformation(const Constraint* const constraint,
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);
346  }
347  *total_demon_runtime = 0;
348 
349  // Gather information.
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;
362  }
363  }
364  }
365 
366  void ExportInformation(const DemonRuns* const demon_runs,
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());
374 
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);
388  }
389  // Compute mean.
390  if (!runtimes.empty()) {
391  *mean_demon_runtime = (1.0L * *total_demon_runtime) / runtimes.size();
392 
393  // Compute median.
394  std::sort(runtimes.begin(), runtimes.end());
395  const int pivot = runtimes.size() / 2;
396 
397  if (runtimes.size() == 1) {
398  *median_demon_runtime = runtimes[0];
399  } else {
400  *median_demon_runtime =
401  runtimes.size() % 2 == 1
402  ? runtimes[pivot]
403  : (runtimes[pivot - 1] + runtimes[pivot]) / 2.0;
404  }
405 
406  // Compute standard deviation.
407  double total_deviation = 0.0f;
408 
409  for (int i = 0; i < runtimes.size(); ++i) {
410  total_deviation += pow(runtimes[i] - *mean_demon_runtime, 2);
411  }
412 
413  *stddev_demon_runtime = sqrt(total_deviation / runtimes.size());
414  }
415  }
416 
417  // The demon_profiler is added by default on the main propagation
418  // monitor. It just needs to be added to the search monitors at the
419  // start of the search.
420  void Install() override { SearchMonitor::Install(); }
421 
422  std::string DebugString() const override { return "DemonProfiler"; }
423 
424  private:
425  Constraint* active_constraint_;
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_;
432 };
433 
434 void Solver::ExportProfilingOverview(const std::string& filename) {
435  if (demon_profiler_ != nullptr) {
436  demon_profiler_->PrintOverview(this, filename);
437  }
438 }
439 
440 // ----- Exported Functions -----
441 
442 void InstallDemonProfiler(DemonProfiler* const monitor) { monitor->Install(); }
443 
445  if (solver->IsProfilingEnabled()) {
446  return new DemonProfiler(solver);
447  } else {
448  return nullptr;
449  }
450 }
451 
452 void DeleteDemonProfiler(DemonProfiler* const monitor) { delete monitor; }
453 
455  CHECK(demon != nullptr);
456  if (InstrumentsDemons()) {
457  propagation_monitor_->RegisterDemon(demon);
458  }
459  return demon;
460 }
461 
462 // ----- Exported Methods for Unit Tests -----
463 
464 void RegisterDemon(Solver* const solver, Demon* const demon,
465  DemonProfiler* const monitor) {
466  monitor->RegisterDemon(demon);
467 }
468 
469 void DemonProfilerAddFakeRun(DemonProfiler* const monitor, Demon* const demon,
470  int64_t start_time, int64_t end_time,
471  bool is_fail) {
472  monitor->AddFakeRun(demon, start_time, end_time, is_fail);
473 }
474 
476  const Constraint* const constraint,
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) {
482  monitor->ExportInformation(constraint, fails, initial_propagation_runtime,
483  demon_invocations, total_demon_runtime,
484  demon_count);
485 }
486 
488  Constraint* const constraint) {
489  monitor->BeginConstraintInitialPropagation(constraint);
490 }
491 
493  Constraint* const constraint) {
494  monitor->EndConstraintInitialPropagation(constraint);
495 }
496 
497 } // namespace operations_research
Definition: base/file.h:33
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 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
void RemoveValue(IntVar *const var, int64_t value) override
DemonProfiler(Solver *const solver)
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.
const int runs
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
GurobiMPCallbackContext * context
int index
Definition: cleanup.h:22
absl::Status WriteString(File *file, const absl::string_view &contents, int flags)
Definition: base/file.cc:194
Options Defaults()
Definition: base/file.h:123
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
Definition: base/file.cc:143
void STLDeleteContainerPairSecondPointers(ForwardIterator begin, ForwardIterator end)
Definition: stl_util.h:353
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)