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