OR-Tools  9.3
demon_profiler.cc
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#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"
35#include "ortools/constraint_solver/demon_profiler.pb.h"
36
37namespace operations_research {
38namespace {
39struct Container {
40 Container(const Constraint* ct_, int64_t value_) : ct(ct_), value(value_) {}
41 bool operator<(const Container& c) const { return value > c.value; }
42
43 const Constraint* ct;
44 int64_t value;
45};
46} // namespace
47
48// DemonProfiler manages the profiling of demons and allows access to gathered
49// data. Add this class as a parameter to Solver and access its information
50// after the end of a search.
52 public:
53 explicit DemonProfiler(Solver* const solver)
55 active_constraint_(nullptr),
56 active_demon_(nullptr),
57 start_time_ns_(absl::GetCurrentTimeNanos()) {}
58
59 ~DemonProfiler() override {
60 gtl::STLDeleteContainerPairSecondPointers(constraint_map_.begin(),
61 constraint_map_.end());
62 }
63
64 // In microseconds.
65 // TODO(user): rename and return nanoseconds.
66 int64_t CurrentTime() const {
67 return (absl::GetCurrentTimeNanos() - start_time_ns_) / 1000;
68 }
69
71 Constraint* const constraint) override {
72 if (solver()->state() == Solver::IN_SEARCH) {
73 return;
74 }
75
76 CHECK(active_constraint_ == nullptr);
77 CHECK(active_demon_ == nullptr);
78 CHECK(constraint != nullptr);
79 ConstraintRuns* const ct_run = new ConstraintRuns;
80 ct_run->set_constraint_id(constraint->DebugString());
81 ct_run->add_initial_propagation_start_time(CurrentTime());
82 active_constraint_ = constraint;
83 constraint_map_[constraint] = ct_run;
84 }
85
86 void EndConstraintInitialPropagation(Constraint* const constraint) override {
87 CHECK(active_constraint_ != nullptr);
88 CHECK(active_demon_ == nullptr);
89 CHECK(constraint != nullptr);
90 CHECK_EQ(constraint, active_constraint_);
91 ConstraintRuns* const ct_run = constraint_map_[constraint];
92 if (ct_run != nullptr) {
93 ct_run->add_initial_propagation_end_time(CurrentTime());
94 ct_run->set_failures(0);
95 }
96 active_constraint_ = nullptr;
97 }
98
100 Constraint* const constraint, Constraint* const delayed) override {
101 if (solver()->state() == Solver::IN_SEARCH) {
102 return;
103 }
104
105 CHECK(active_constraint_ == nullptr);
106 CHECK(active_demon_ == nullptr);
107 CHECK(constraint != nullptr);
108 CHECK(delayed != nullptr);
109 ConstraintRuns* const ct_run = constraint_map_[constraint];
110 ct_run->add_initial_propagation_start_time(CurrentTime());
111 active_constraint_ = constraint;
112 }
113
115 Constraint* const constraint, Constraint* const delayed) override {
116 CHECK(active_constraint_ != nullptr);
117 CHECK(active_demon_ == nullptr);
118 CHECK(constraint != nullptr);
119 CHECK(delayed != nullptr);
120 CHECK_EQ(constraint, active_constraint_);
121 ConstraintRuns* const ct_run = constraint_map_[constraint];
122 if (ct_run != nullptr) {
123 ct_run->add_initial_propagation_end_time(CurrentTime());
124 ct_run->set_failures(0);
125 }
126 active_constraint_ = nullptr;
127 }
128
129 void RegisterDemon(Demon* const demon) override {
130 if (solver()->state() == Solver::IN_SEARCH) {
131 return;
132 }
133
134 if (demon_map_.find(demon) == demon_map_.end()) {
135 CHECK(active_constraint_ != nullptr);
136 CHECK(active_demon_ == nullptr);
137 CHECK(demon != nullptr);
138 ConstraintRuns* const ct_run = constraint_map_[active_constraint_];
139 DemonRuns* const demon_run = ct_run->add_demons();
140 demon_run->set_demon_id(demon->DebugString());
141 demon_run->set_failures(0);
142 demon_map_[demon] = demon_run;
143 demons_per_constraint_[active_constraint_].push_back(demon_run);
144 }
145 }
146
147 void BeginDemonRun(Demon* const demon) override {
148 CHECK(demon != nullptr);
149 if (demon->priority() == Solver::VAR_PRIORITY) {
150 return;
151 }
152 CHECK(active_demon_ == nullptr);
153 active_demon_ = demon;
154 DemonRuns* const demon_run = demon_map_[active_demon_];
155 if (demon_run != nullptr) {
156 demon_run->add_start_time(CurrentTime());
157 }
158 }
159
160 void EndDemonRun(Demon* const demon) override {
161 CHECK(demon != nullptr);
162 if (demon->priority() == Solver::VAR_PRIORITY) {
163 return;
164 }
165 CHECK_EQ(active_demon_, demon);
166 DemonRuns* const demon_run = demon_map_[active_demon_];
167 if (demon_run != nullptr) {
168 demon_run->add_end_time(CurrentTime());
169 }
170 active_demon_ = nullptr;
171 }
172
174 void EndProcessingIntegerVariable(IntVar* const var) override {}
175 void PushContext(const std::string& context) override {}
176 void PopContext() override {}
177
178 void BeginFail() override {
179 if (active_demon_ != nullptr) {
180 DemonRuns* const demon_run = demon_map_[active_demon_];
181 if (demon_run != nullptr) {
182 demon_run->add_end_time(CurrentTime());
183 demon_run->set_failures(demon_run->failures() + 1);
184 }
185 active_demon_ = nullptr;
186 // active_constraint_ can be non null in case of initial propagation.
187 active_constraint_ = nullptr;
188 } else if (active_constraint_ != nullptr) {
189 ConstraintRuns* const ct_run = constraint_map_[active_constraint_];
190 if (ct_run != nullptr) {
191 ct_run->add_initial_propagation_end_time(CurrentTime());
192 ct_run->set_failures(1);
193 }
194 active_constraint_ = nullptr;
195 }
196 }
197
198 // Restarts a search and clears all previously collected information.
199 void RestartSearch() override {
200 gtl::STLDeleteContainerPairSecondPointers(constraint_map_.begin(),
201 constraint_map_.end());
202 constraint_map_.clear();
203 demon_map_.clear();
204 demons_per_constraint_.clear();
205 }
206
207 // IntExpr modifiers.
208 void SetMin(IntExpr* const expr, int64_t new_min) override {}
209 void SetMax(IntExpr* const expr, int64_t new_max) override {}
210 void SetRange(IntExpr* const expr, int64_t new_min,
211 int64_t new_max) override {}
212 // IntVar modifiers.
213 void SetMin(IntVar* const var, int64_t new_min) override {}
214 void SetMax(IntVar* const var, int64_t new_max) override {}
215 void SetRange(IntVar* const var, int64_t new_min, int64_t new_max) override {}
216 void RemoveValue(IntVar* const var, int64_t value) override {}
217 void SetValue(IntVar* const var, int64_t value) override {}
218 void RemoveInterval(IntVar* const var, int64_t imin, int64_t imax) override {}
219 void SetValues(IntVar* const var,
220 const std::vector<int64_t>& values) override {}
222 const std::vector<int64_t>& values) override {}
223 // IntervalVar modifiers.
224 void SetStartMin(IntervalVar* const var, int64_t new_min) override {}
225 void SetStartMax(IntervalVar* const var, int64_t new_max) override {}
226 void SetStartRange(IntervalVar* const var, int64_t new_min,
227 int64_t new_max) override {}
228 void SetEndMin(IntervalVar* const var, int64_t new_min) override {}
229 void SetEndMax(IntervalVar* const var, int64_t new_max) override {}
230 void SetEndRange(IntervalVar* const var, int64_t new_min,
231 int64_t new_max) override {}
232 void SetDurationMin(IntervalVar* const var, int64_t new_min) override {}
233 void SetDurationMax(IntervalVar* const var, int64_t new_max) override {}
234 void SetDurationRange(IntervalVar* const var, int64_t new_min,
235 int64_t new_max) override {}
236 void SetPerformed(IntervalVar* const var, bool value) override {}
237 void RankFirst(SequenceVar* const var, int index) override {}
238 void RankNotFirst(SequenceVar* const var, int index) override {}
239 void RankLast(SequenceVar* const var, int index) override {}
240 void RankNotLast(SequenceVar* const var, int index) override {}
241 void RankSequence(SequenceVar* const var, const std::vector<int>& rank_first,
242 const std::vector<int>& rank_last,
243 const std::vector<int>& unperformed) override {}
244
245 // Useful for unit tests.
246 void AddFakeRun(Demon* const demon, int64_t start_time, int64_t end_time,
247 bool is_fail) {
248 CHECK(demon != nullptr);
249 DemonRuns* const demon_run = demon_map_[demon];
250 CHECK(demon_run != nullptr);
251 demon_run->add_start_time(start_time);
252 demon_run->add_end_time(end_time);
253 if (is_fail) {
254 demon_run->set_failures(demon_run->failures() + 1);
255 }
256 }
257
258 // Exports collected data as human-readable text.
259 void PrintOverview(Solver* const solver, const std::string& filename) {
260 const char* const kConstraintFormat =
261 " - Constraint: %s\n failures=%d, initial propagation "
262 "runtime=%d us, demons=%d, demon invocations=%d, total demon "
263 "runtime=%d us\n";
264 const char* const kDemonFormat =
265 " --- Demon: %s\n invocations=%d, failures=%d, total "
266 "runtime=%d us, [average=%.2lf, median=%.2lf, stddev=%.2lf]\n";
267 File* file;
268 const std::string model =
269 absl::StrFormat("Model %s:\n", solver->model_name());
270 if (file::Open(filename, "w", &file, file::Defaults()).ok()) {
271 file::WriteString(file, model, file::Defaults()).IgnoreError();
272 std::vector<Container> to_sort;
273 for (absl::flat_hash_map<const Constraint*,
274 ConstraintRuns*>::const_iterator it =
275 constraint_map_.begin();
276 it != constraint_map_.end(); ++it) {
277 const Constraint* const ct = it->first;
278 int64_t fails = 0;
279 int64_t demon_invocations = 0;
280 int64_t initial_propagation_runtime = 0;
281 int64_t total_demon_runtime = 0;
282 int demon_count = 0;
283 ExportInformation(ct, &fails, &initial_propagation_runtime,
284 &demon_invocations, &total_demon_runtime,
285 &demon_count);
286 to_sort.push_back(
287 Container(ct, total_demon_runtime + initial_propagation_runtime));
288 }
289 std::sort(to_sort.begin(), to_sort.end());
290
291 for (int i = 0; i < to_sort.size(); ++i) {
292 const Constraint* const ct = to_sort[i].ct;
293 int64_t fails = 0;
294 int64_t demon_invocations = 0;
295 int64_t initial_propagation_runtime = 0;
296 int64_t total_demon_runtime = 0;
297 int demon_count = 0;
298 ExportInformation(ct, &fails, &initial_propagation_runtime,
299 &demon_invocations, &total_demon_runtime,
300 &demon_count);
301 const std::string constraint_message =
302 absl::StrFormat(kConstraintFormat, ct->DebugString(), fails,
303 initial_propagation_runtime, demon_count,
304 demon_invocations, total_demon_runtime);
305 file::WriteString(file, constraint_message, file::Defaults())
306 .IgnoreError();
307 const std::vector<DemonRuns*>& demons = demons_per_constraint_[ct];
308 const int demon_size = demons.size();
309 for (int demon_index = 0; demon_index < demon_size; ++demon_index) {
310 DemonRuns* const demon_runs = demons[demon_index];
311 int64_t invocations = 0;
312 int64_t fails = 0;
313 int64_t runtime = 0;
314 double mean_runtime = 0;
315 double median_runtime = 0;
316 double standard_deviation = 0.0;
317 ExportInformation(demon_runs, &invocations, &fails, &runtime,
318 &mean_runtime, &median_runtime,
319 &standard_deviation);
320 const std::string runs = absl::StrFormat(
321 kDemonFormat, demon_runs->demon_id(), invocations, fails, runtime,
322 mean_runtime, median_runtime, standard_deviation);
323 file::WriteString(file, runs, file::Defaults()).IgnoreError();
324 }
325 }
326 }
327 file->Close(file::Defaults()).IgnoreError();
328 }
329
330 // Export Information
331 void ExportInformation(const Constraint* const constraint,
332 int64_t* const fails,
333 int64_t* const initial_propagation_runtime,
334 int64_t* const demon_invocations,
335 int64_t* const total_demon_runtime, int* demons) {
336 CHECK(constraint != nullptr);
337 ConstraintRuns* const ct_run = constraint_map_[constraint];
338 CHECK(ct_run != nullptr);
339 *demon_invocations = 0;
340 *fails = ct_run->failures();
341 *initial_propagation_runtime = 0;
342 for (int i = 0; i < ct_run->initial_propagation_start_time_size(); ++i) {
343 *initial_propagation_runtime += ct_run->initial_propagation_end_time(i) -
344 ct_run->initial_propagation_start_time(i);
345 }
346 *total_demon_runtime = 0;
347
348 // Gather information.
349 *demons = ct_run->demons_size();
350 CHECK_EQ(*demons, demons_per_constraint_[constraint].size());
351 for (int demon_index = 0; demon_index < *demons; ++demon_index) {
352 const DemonRuns& demon_runs = ct_run->demons(demon_index);
353 *fails += demon_runs.failures();
354 CHECK_EQ(demon_runs.start_time_size(), demon_runs.end_time_size());
355 const int runs = demon_runs.start_time_size();
356 *demon_invocations += runs;
357 for (int run_index = 0; run_index < runs; ++run_index) {
358 const int64_t demon_time =
359 demon_runs.end_time(run_index) - demon_runs.start_time(run_index);
360 *total_demon_runtime += demon_time;
361 }
362 }
363 }
364
365 void ExportInformation(const DemonRuns* const demon_runs,
366 int64_t* const demon_invocations, int64_t* const fails,
367 int64_t* const total_demon_runtime,
368 double* const mean_demon_runtime,
369 double* const median_demon_runtime,
370 double* const stddev_demon_runtime) {
371 CHECK(demon_runs != nullptr);
372 CHECK_EQ(demon_runs->start_time_size(), demon_runs->end_time_size());
373
374 const int runs = demon_runs->start_time_size();
375 *demon_invocations = runs;
376 *fails = demon_runs->failures();
377 *total_demon_runtime = 0;
378 *mean_demon_runtime = 0.0;
379 *median_demon_runtime = 0.0;
380 *stddev_demon_runtime = 0.0;
381 std::vector<double> runtimes;
382 for (int run_index = 0; run_index < runs; ++run_index) {
383 const int64_t demon_time =
384 demon_runs->end_time(run_index) - demon_runs->start_time(run_index);
385 *total_demon_runtime += demon_time;
386 runtimes.push_back(demon_time);
387 }
388 // Compute mean.
389 if (!runtimes.empty()) {
390 *mean_demon_runtime = (1.0L * *total_demon_runtime) / runtimes.size();
391
392 // Compute median.
393 std::sort(runtimes.begin(), runtimes.end());
394 const int pivot = runtimes.size() / 2;
395
396 if (runtimes.size() == 1) {
397 *median_demon_runtime = runtimes[0];
398 } else {
399 *median_demon_runtime =
400 runtimes.size() % 2 == 1
401 ? runtimes[pivot]
402 : (runtimes[pivot - 1] + runtimes[pivot]) / 2.0;
403 }
404
405 // Compute standard deviation.
406 double total_deviation = 0.0f;
407
408 for (int i = 0; i < runtimes.size(); ++i) {
409 total_deviation += pow(runtimes[i] - *mean_demon_runtime, 2);
410 }
411
412 *stddev_demon_runtime = sqrt(total_deviation / runtimes.size());
413 }
414 }
415
416 // The demon_profiler is added by default on the main propagation
417 // monitor. It just needs to be added to the search monitors at the
418 // start of the search.
419 void Install() override { SearchMonitor::Install(); }
420
421 std::string DebugString() const override { return "DemonProfiler"; }
422
423 private:
424 Constraint* active_constraint_;
425 Demon* active_demon_;
426 const int64_t start_time_ns_;
427 absl::flat_hash_map<const Constraint*, ConstraintRuns*> constraint_map_;
428 absl::flat_hash_map<const Demon*, DemonRuns*> demon_map_;
429 absl::flat_hash_map<const Constraint*, std::vector<DemonRuns*> >
430 demons_per_constraint_;
431};
432
433void Solver::ExportProfilingOverview(const std::string& filename) {
434 if (demon_profiler_ != nullptr) {
435 demon_profiler_->PrintOverview(this, filename);
436 }
437}
438
439// ----- Exported Functions -----
440
441void InstallDemonProfiler(DemonProfiler* const monitor) { monitor->Install(); }
442
444 if (solver->IsProfilingEnabled()) {
445 return new DemonProfiler(solver);
446 } else {
447 return nullptr;
448 }
449}
450
451void DeleteDemonProfiler(DemonProfiler* const monitor) { delete monitor; }
452
454 CHECK(demon != nullptr);
455 if (InstrumentsDemons()) {
456 propagation_monitor_->RegisterDemon(demon);
457 }
458 return demon;
459}
460
461// ----- Exported Methods for Unit Tests -----
462
463void RegisterDemon(Solver* const solver, Demon* const demon,
464 DemonProfiler* const monitor) {
465 monitor->RegisterDemon(demon);
466}
467
468void DemonProfilerAddFakeRun(DemonProfiler* const monitor, Demon* const demon,
469 int64_t start_time, int64_t end_time,
470 bool is_fail) {
471 monitor->AddFakeRun(demon, start_time, end_time, is_fail);
472}
473
475 const Constraint* const constraint,
476 int64_t* const fails,
477 int64_t* const initial_propagation_runtime,
478 int64_t* const demon_invocations,
479 int64_t* const total_demon_runtime,
480 int* const demon_count) {
481 monitor->ExportInformation(constraint, fails, initial_propagation_runtime,
482 demon_invocations, total_demon_runtime,
483 demon_count);
484}
485
487 Constraint* const constraint) {
488 monitor->BeginConstraintInitialPropagation(constraint);
489}
490
492 Constraint* const constraint) {
493 monitor->EndConstraintInitialPropagation(constraint);
494}
495
496} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
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:184
int Defaults()
Definition: base/file.h:120
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
Definition: base/file.cc:142
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)