OR-Tools  8.0
stats.h
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 // Helper classes to track statistics of a program component.
15 //
16 // Usage example:
17 // // Suppose you have a class that contains a factorization of a matrix B and
18 // // a Solve() function to solve the linear system B.x = a.
19 //
20 // // You will hold your stats in a Stats stats_ class member:
21 // struct Stats : public StatsGroup {
22 // Stats() : StatsGroup("BasisFactorization"),
23 // solve_time("solve_time", this),
24 // input_vector_density("input_vector_density", this),
25 // estimated_accuracy("estimated_accuracy", this) {}
26 //
27 // TimeDistribution solve_time;
28 // RatioDistribution input_vector_density;
29 //
30 // // Values of a few components of B.x - a, updated on each solve.
31 // DoubleDistribution estimated_accuracy;
32 // }
33 //
34 // // You then add a few lines to your Solve() function:
35 // void Solve() {
36 // stats_.solve_time.StartTimer();
37 // stats_.input_vector_density.Add(ComputeDensity());
38 // ... // Do the work.
39 // stats_.estimated_accuracy.Add(EstimateAccuracy());
40 // stats_.solve_time.StopTimerAndAddElapsedTime();
41 // }
42 //
43 // // Now, calling stats_.StatString() will give you a summary of your stats:
44 // BasisFactorization {
45 // solve_time : num [min, max] average std_deviation total
46 // input_vector_density : num [min, max] average std_deviation
47 // estimated_accuracy : num [min, max] average std_deviation
48 // }
49 //
50 // For measuring time, another alternative is to use the SCOPED_TIME_STAT macro.
51 // In our example above, you don't need to define the solve_time distribution
52 // and you can just do:
53 //
54 // void Solve() {
55 // SCOPED_TIME_STAT(&stats_);
56 // ...
57 // }
58 //
59 // This automatically adds a TimeDistribution with name "Solve" to stats_ and
60 // times your function calls!
61 //
62 // IMPORTANT: The SCOPED_TIME_STAT() macro only does something if OR_STATS is
63 // defined, so you need to build your code with blaze build --copt='-DOR_STATS'.
64 // The idea is that by default the instrumentation is off. You can also use the
65 // macro IF_STATS_ENABLED() that does nothing if OR_STATS is not defined or just
66 // translates to its argument otherwise.
67 
68 #ifndef OR_TOOLS_UTIL_STATS_H_
69 #define OR_TOOLS_UTIL_STATS_H_
70 
71 #include <map>
72 #include <string>
73 
74 #ifdef HAS_PERF_SUBSYSTEM
75 #include "absl/strings/str_replace.h"
76 #include "exegesis/exegesis/itineraries/perf_subsystem.h"
78 #endif // HAS_PERF_SUBSYSTEM
79 
80 #include "ortools/base/macros.h"
81 #include "ortools/base/timer.h"
82 
83 namespace operations_research {
84 
85 // Returns the current thread's total memory usage in an human-readable string.
86 std::string MemoryUsage();
87 
88 // Forward declaration.
89 class StatsGroup;
90 class TimeDistribution;
91 
92 // Base class for a statistic that can be pretty-printed.
93 class Stat {
94  public:
95  explicit Stat(const std::string& name) : name_(name) {}
96 
97  // Also add this stat to the given group.
98  Stat(const std::string& name, StatsGroup* group);
99  virtual ~Stat() {}
100 
101  // Only used for display purposes.
102  std::string Name() const { return name_; }
103 
104  // Returns a human-readable formatted line of the form "name:
105  // ValueAsString()".
106  std::string StatString() const;
107 
108  // At display, stats are displayed by decreasing priority, then decreasing
109  // Sum(), then alphabetical order.
110  // Used to group the stats per category (timing, ratio, etc..,).
111  virtual int Priority() const { return 0; }
112 
113  // By default return 0 for the sum. This makes it possible to sort stats by
114  // decreasing total time.
115  virtual double Sum() const { return 0; }
116 
117  // Prints information about this statistic.
118  virtual std::string ValueAsString() const = 0;
119 
120  // Is this stat worth printing? Usually false if nothing was measured.
121  virtual bool WorthPrinting() const = 0;
122 
123  // Reset this statistic to the same state as if it was newly created.
124  virtual void Reset() = 0;
125 
126  private:
127  const std::string name_;
128 };
129 
130 // Base class to print a nice summary of a group of statistics.
131 class StatsGroup {
132  public:
133  enum PrintOrder {
136  };
137 
138  explicit StatsGroup(const std::string& name)
139  : name_(name), stats_(), time_distributions_() {}
140  ~StatsGroup();
141 
142  // Registers a Stat, which will appear in the string returned by StatString().
143  // The Stat object must live as long as this StatsGroup.
144  void Register(Stat* stat);
145 
146  // Returns this group name, followed by one line per Stat registered with this
147  // group (this includes the ones created by LookupOrCreateTimeDistribution()).
148  // Note that only the stats WorthPrinting() are printed.
149  std::string StatString() const;
150 
151  // Changes the print ordering (will affect the order in which the stats
152  // registered with this group are printed via StatString()).
153  void SetPrintOrder(PrintOrder print_order) { print_order_ = print_order; }
154 
155  // Returns and if needed creates and registers a TimeDistribution with the
156  // given name. Note that this involve a map lookup and his thus slower than
157  // directly accessing a TimeDistribution variable.
159 
160  // Calls Reset() on all the statistics registered with this group.
161  void Reset();
162 
163  private:
164  std::string name_;
166  std::vector<Stat*> stats_;
167  std::map<std::string, TimeDistribution*> time_distributions_;
168 
169  DISALLOW_COPY_AND_ASSIGN(StatsGroup);
170 };
171 
172 // Base class to track and compute statistics about the distribution of a
173 // sequence of double. We provide a few sub-classes below that differ in the way
174 // the values are added to the sequence and in the way the stats are printed.
175 class DistributionStat : public Stat {
176  public:
177  explicit DistributionStat(const std::string& name);
178  DistributionStat(const std::string& name, StatsGroup* group);
179  ~DistributionStat() override {}
180  void Reset() override;
181  bool WorthPrinting() const override { return num_ != 0; }
182 
183  // Implemented by the subclasses.
184  std::string ValueAsString() const override = 0;
185 
186  // Trivial statistics on all the values added so far.
187  double Sum() const override { return sum_; }
188  double Max() const { return max_; }
189  double Min() const { return min_; }
190  int64 Num() const { return num_; }
191 
192  // Get the average of the distribution or 0.0 if empty.
193  double Average() const;
194 
195  // Get the standard deviation of the distribution or 0.0 if empty.
196  // We use the on-line algorithm of Welford described at
197  // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
198  // TODO(user): We could also use on top the Kahan summation algorithm to be
199  // even more precise but a bit slower too.
200  double StdDeviation() const;
201 
202  protected:
203  // Adds a value to this sequence and updates the stats.
204  void AddToDistribution(double value);
205  double sum_;
206  double average_;
208  double min_;
209  double max_;
211 };
212 
213 // Statistic on the distribution of a sequence of running times.
214 // Also provides some facility to measure such time with the CPU cycle counter.
215 //
216 // TODO(user): Since we inherit from DistributionStat, we currently store the
217 // sum of CPU cycles as a double internally. A better option is to use int64
218 // because with the 53 bits of precision of a double, we will run into an issue
219 // if the sum of times reaches 52 days for a 2GHz processor.
221  public:
222  explicit TimeDistribution(const std::string& name)
223  : DistributionStat(name), timer_() {}
224  TimeDistribution(const std::string& name, StatsGroup* group)
225  : DistributionStat(name, group), timer_() {}
226  std::string ValueAsString() const override;
227 
228  // Time distributions have a high priority to be displayed first.
229  int Priority() const override { return 100; }
230 
231  // Internaly the TimeDistribution stores CPU cycles (to do a bit less work
232  // on each StopTimerAndAddElapsedTime()). Use this function to convert
233  // all the statistics of DistributionStat into seconds.
234  static double CyclesToSeconds(double num_cycles);
235 
236  // Adds a time in seconds to this distribution.
237  void AddTimeInSec(double seconds);
238 
239  // Adds a time in CPU cycles to this distribution.
240  void AddTimeInCycles(double cycles);
241 
242  // Starts the timer in preparation of a StopTimerAndAddElapsedTime().
243  inline void StartTimer() { timer_.Restart(); }
244 
245  // Adds the elapsed time since the last StartTimer() to the distribution and
246  // returns this time in CPU cycles.
247  inline double StopTimerAndAddElapsedTime() {
248  const double cycles = static_cast<double>(timer_.GetCycles());
249  AddToDistribution(cycles);
250  return cycles;
251  }
252 
253  private:
254  // Converts and prints a number of cycles in an human readable way using the
255  // proper time unit depending on the value (ns, us, ms, s, m or h).
256  static std::string PrintCyclesAsTime(double cycles);
257  CycleTimer timer_;
258 };
259 
260 // Statistic on the distribution of a sequence of ratios, displayed as %.
262  public:
263  explicit RatioDistribution(const std::string& name)
264  : DistributionStat(name) {}
265  RatioDistribution(const std::string& name, StatsGroup* group)
266  : DistributionStat(name, group) {}
267  std::string ValueAsString() const override;
268  void Add(double value);
269 };
270 
271 // Statistic on the distribution of a sequence of doubles.
273  public:
274  explicit DoubleDistribution(const std::string& name)
275  : DistributionStat(name) {}
276  DoubleDistribution(const std::string& name, StatsGroup* group)
277  : DistributionStat(name, group) {}
278  std::string ValueAsString() const override;
279  void Add(double value);
280 };
281 
282 // Statistic on the distribution of a sequence of integers.
284  public:
285  explicit IntegerDistribution(const std::string& name)
286  : DistributionStat(name) {}
287  IntegerDistribution(const std::string& name, StatsGroup* group)
288  : DistributionStat(name, group) {}
289  std::string ValueAsString() const override;
290  void Add(int64 value);
291 };
292 
293 // Helper classes to time a block of code and add the result to a
294 // TimeDistribution. Calls StartTimer() on creation and
295 // StopTimerAndAddElapsedTime() on destruction.
296 //
297 // There are three classes with the same interface:
298 // * EnabledScopedTimeDistributionUpdater always collects the time stats of the
299 // scope in which it is defined. This class is used for stats that are always
300 // collected.
301 // * ScopedTimeDistributionUpdater collects the time stats only when OR_STATS is
302 // defined. This symbol should be used for collecting stats in places where
303 // the overhead of collecting the stats may hurt the performance of the
304 // algorithm.
305 // * DisabledScopedTimeDistributionUpdater is used to implement
306 // ScopedTimeDistributionUpdater when OR_STATS is not defined.
308  public:
309  // Note that this does not take ownership of the given stat.
311  : stat_(stat), also_update_(nullptr) {
312  stat->StartTimer();
313  }
315  const double cycles = stat_->StopTimerAndAddElapsedTime();
316  if (also_update_ != nullptr) {
317  also_update_->AddTimeInCycles(cycles);
318  }
319  }
320 
321  // Updates another TimeDistribution on destruction. This is useful to split
322  // a total time measurement in different categories:
323  //
324  // EnabledScopedTimeDistributionUpdater timer(&total_timer);
325  // ...
326  // switch (type) {
327  // case TypeA : timer.AlsoUpdate(&typeA_timer); break;
328  // case TypeB : timer.AlsoUpdate(&typeB_timer); break;
329  // }
330  void AlsoUpdate(TimeDistribution* also_update) { also_update_ = also_update; }
331 
332  private:
333  TimeDistribution* stat_;
334  TimeDistribution* also_update_;
335  DISALLOW_COPY_AND_ASSIGN(EnabledScopedTimeDistributionUpdater);
336 };
337 
339  public:
341  void AlsoUpdate(TimeDistribution* also_update) {}
342 
343  private:
344  DISALLOW_COPY_AND_ASSIGN(DisabledScopedTimeDistributionUpdater);
345 };
346 
347 #ifdef HAS_PERF_SUBSYSTEM
348 // Helper classes to count instructions during execution of a block of code and
349 // add print the results to logs.
350 //
351 // Note: To enable instruction counting on machines running Debian, execute the
352 // following commands to modify the permissions.
353 // sudo echo "1" > /proc/sys/kernel/perf_event_paranoid
354 // sudo echo "0" > /proc/sys/kernel/kptr_restrict
355 class EnabledScopedInstructionCounter {
356  public:
357  explicit EnabledScopedInstructionCounter(const std::string& name,
358  TimeLimit* time_limit);
359  EnabledScopedInstructionCounter(const EnabledScopedInstructionCounter&) =
360  delete;
361  EnabledScopedInstructionCounter& operator=(
362  const EnabledScopedInstructionCounter&) = delete;
363  ~EnabledScopedInstructionCounter();
364 
365  // Used only for testing.
366  double ReadInstructionCount() { return ending_count_ - starting_count_; }
367 
368  private:
369  TimeLimit* time_limit_;
370  std::string name_;
371  double starting_count_;
372  double ending_count_;
373 };
374 #endif // HAS_PERF_SUBSYSTEM
375 
377  public:
378  explicit DisabledScopedInstructionCounter(const std::string& name) {}
380  delete;
382  const DisabledScopedInstructionCounter&) = delete;
383 };
384 
385 #ifdef OR_STATS
386 
388 #ifdef HAS_PERF_SUBSYSTEM
389 using ScopedInstructionCounter = EnabledScopedInstructionCounter;
390 #else // HAS_PERF_SUBSYSTEM
392 #endif // HAS_PERF_SUBSYSTEM
393 
394 // Simple macro to be used by a client that want to execute costly operations
395 // only if OR_STATS is defined.
396 #define IF_STATS_ENABLED(instructions) instructions
397 
398 // Measures the time from this macro line to the end of the scope and adds it
399 // to the distribution (from the given StatsGroup) with the same name as the
400 // enclosing function.
401 //
402 // Note(user): This adds more extra overhead around the measured code compared
403 // to defining your own TimeDistribution stat in your StatsGroup. About 80ns
404 // per measurement compared to about 20ns (as of 2012-06, on my workstation).
405 #define SCOPED_TIME_STAT(stats) \
406  operations_research::ScopedTimeDistributionUpdater scoped_time_stat( \
407  (stats)->LookupOrCreateTimeDistribution(__FUNCTION__))
408 
409 #ifdef HAS_PERF_SUBSYSTEM
410 
411 inline std::string RemoveOperationsResearchAndGlop(
412  const std::string& pretty_function) {
413  return strings::GlobalReplaceSubstrings(
414  pretty_function, {{"operations_research::", ""}, {"glop::", ""}});
415 }
416 
417 #define SCOPED_INSTRUCTION_COUNT(time_limit) \
418  operations_research::ScopedInstructionCounter scoped_instruction_count( \
419  RemoveOperationsResearchAndGlop(__PRETTY_FUNCTION__), time_limit)
420 
421 #endif // HAS_PERF_SUBSYSTEM
422 
423 #else // OR_STATS
424 // If OR_STATS is not defined, we remove some instructions that may be time
425 // consuming.
426 
429 
430 #define IF_STATS_ENABLED(instructions)
431 #define SCOPED_TIME_STAT(stats)
432 #define SCOPED_INSTRUCTION_COUNT(time_limit)
433 
434 #endif // OR_STATS
435 
436 } // namespace operations_research
437 
438 #endif // OR_TOOLS_UTIL_STATS_H_
operations_research::TimeDistribution::StartTimer
void StartTimer()
Definition: stats.h:243
operations_research::StatsGroup::PrintOrder
PrintOrder
Definition: stats.h:133
operations_research::RatioDistribution
Definition: stats.h:261
operations_research::TimeDistribution::ValueAsString
std::string ValueAsString() const override
Definition: stats.cc:204
operations_research::StatsGroup
Definition: stats.h:131
operations_research::Stat::Reset
virtual void Reset()=0
operations_research::Stat::Name
std::string Name() const
Definition: stats.h:102
time_limit.h
operations_research::Stat::StatString
std::string StatString() const
Definition: stats.cc:46
operations_research::TimeDistribution::TimeDistribution
TimeDistribution(const std::string &name)
Definition: stats.h:222
operations_research::RatioDistribution::ValueAsString
std::string ValueAsString() const override
Definition: stats.cc:216
operations_research::Stat
Definition: stats.h:93
operations_research::DistributionStat::max_
double max_
Definition: stats.h:209
operations_research::DisabledScopedTimeDistributionUpdater::AlsoUpdate
void AlsoUpdate(TimeDistribution *also_update)
Definition: stats.h:341
operations_research::RatioDistribution::RatioDistribution
RatioDistribution(const std::string &name)
Definition: stats.h:263
operations_research::DistributionStat::sum_
double sum_
Definition: stats.h:205
value
int64 value
Definition: demon_profiler.cc:43
operations_research::TimeDistribution::StopTimerAndAddElapsedTime
double StopTimerAndAddElapsedTime()
Definition: stats.h:247
macros.h
operations_research::DistributionStat::Reset
void Reset() override
Definition: stats.cc:141
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::DisabledScopedTimeDistributionUpdater::DisabledScopedTimeDistributionUpdater
DisabledScopedTimeDistributionUpdater(TimeDistribution *stat)
Definition: stats.h:340
operations_research::StatsGroup::SORT_BY_PRIORITY_THEN_VALUE
@ SORT_BY_PRIORITY_THEN_VALUE
Definition: stats.h:134
operations_research::DistributionStat::average_
double average_
Definition: stats.h:206
operations_research::IntegerDistribution::Add
void Add(int64 value)
Definition: stats.cc:229
operations_research::MemoryUsage
std::string MemoryUsage()
Definition: stats.cc:25
operations_research::DistributionStat::AddToDistribution
void AddToDistribution(double value)
Definition: stats.cc:150
operations_research::DistributionStat::Num
int64 Num() const
Definition: stats.h:190
operations_research::DisabledScopedInstructionCounter
Definition: stats.h:376
int64
int64_t int64
Definition: integral_types.h:34
operations_research::TimeDistribution::CyclesToSeconds
static double CyclesToSeconds(double num_cycles)
Definition: stats.cc:175
operations_research::DistributionStat::Average
double Average() const
Definition: stats.cc:168
operations_research::IntegerDistribution::IntegerDistribution
IntegerDistribution(const std::string &name, StatsGroup *group)
Definition: stats.h:287
operations_research::TimeDistribution::Priority
int Priority() const override
Definition: stats.h:229
operations_research::EnabledScopedTimeDistributionUpdater::~EnabledScopedTimeDistributionUpdater
~EnabledScopedTimeDistributionUpdater()
Definition: stats.h:314
operations_research::Stat::Priority
virtual int Priority() const
Definition: stats.h:111
operations_research::DisabledScopedTimeDistributionUpdater
Definition: stats.h:338
operations_research::DisabledScopedInstructionCounter::operator=
DisabledScopedInstructionCounter & operator=(const DisabledScopedInstructionCounter &)=delete
operations_research::Stat::ValueAsString
virtual std::string ValueAsString() const =0
operations_research::DistributionStat::WorthPrinting
bool WorthPrinting() const override
Definition: stats.h:181
WallTimer::Restart
void Restart()
Definition: timer.h:35
operations_research::StatsGroup::SORT_BY_NAME
@ SORT_BY_NAME
Definition: stats.h:135
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2025
CycleTimer::GetCycles
int64 GetCycles() const
Definition: timer.h:76
operations_research::DoubleDistribution::DoubleDistribution
DoubleDistribution(const std::string &name)
Definition: stats.h:274
operations_research::DistributionStat::DistributionStat
DistributionStat(const std::string &name)
Definition: stats.cc:123
operations_research::IntegerDistribution::IntegerDistribution
IntegerDistribution(const std::string &name)
Definition: stats.h:285
operations_research::RatioDistribution::RatioDistribution
RatioDistribution(const std::string &name, StatsGroup *group)
Definition: stats.h:265
operations_research::StatsGroup::Reset
void Reset()
Definition: stats.cc:52
timer.h
operations_research::DistributionStat::ValueAsString
std::string ValueAsString() const override=0
operations_research::DistributionStat::Min
double Min() const
Definition: stats.h:189
operations_research::Stat::Stat
Stat(const std::string &name)
Definition: stats.h:95
operations_research::StatsGroup::LookupOrCreateTimeDistribution
TimeDistribution * LookupOrCreateTimeDistribution(std::string name)
Definition: stats.cc:114
operations_research::TimeDistribution
Definition: stats.h:220
operations_research::EnabledScopedTimeDistributionUpdater::AlsoUpdate
void AlsoUpdate(TimeDistribution *also_update)
Definition: stats.h:330
operations_research::TimeDistribution::AddTimeInSec
void AddTimeInSec(double seconds)
Definition: stats.cc:193
operations_research::StatsGroup::StatsGroup
StatsGroup(const std::string &name)
Definition: stats.h:138
operations_research::StatsGroup::Register
void Register(Stat *stat)
Definition: stats.cc:50
operations_research::Stat::Sum
virtual double Sum() const
Definition: stats.h:115
operations_research::StatsGroup::SetPrintOrder
void SetPrintOrder(PrintOrder print_order)
Definition: stats.h:153
CycleTimer
Definition: timer.h:72
operations_research::DistributionStat::num_
int64 num_
Definition: stats.h:210
operations_research::StatsGroup::~StatsGroup
~StatsGroup()
Definition: stats.cc:48
operations_research::DoubleDistribution::Add
void Add(double value)
Definition: stats.cc:222
operations_research::DistributionStat::sum_squares_from_average_
double sum_squares_from_average_
Definition: stats.h:207
operations_research::DistributionStat::Sum
double Sum() const override
Definition: stats.h:187
operations_research::EnabledScopedTimeDistributionUpdater::EnabledScopedTimeDistributionUpdater
EnabledScopedTimeDistributionUpdater(TimeDistribution *stat)
Definition: stats.h:310
operations_research::DistributionStat::min_
double min_
Definition: stats.h:208
operations_research::DoubleDistribution
Definition: stats.h:272
operations_research::DisabledScopedInstructionCounter::DisabledScopedInstructionCounter
DisabledScopedInstructionCounter(const DisabledScopedInstructionCounter &)=delete
operations_research::IntegerDistribution::ValueAsString
std::string ValueAsString() const override
Definition: stats.cc:233
operations_research::StatsGroup::StatString
std::string StatString() const
Definition: stats.cc:71
operations_research::DisabledScopedInstructionCounter::DisabledScopedInstructionCounter
DisabledScopedInstructionCounter(const std::string &name)
Definition: stats.h:378
operations_research::EnabledScopedTimeDistributionUpdater
Definition: stats.h:307
operations_research::IntegerDistribution
Definition: stats.h:283
operations_research::TimeDistribution::TimeDistribution
TimeDistribution(const std::string &name, StatsGroup *group)
Definition: stats.h:224
operations_research::DistributionStat::~DistributionStat
~DistributionStat() override
Definition: stats.h:179
operations_research::DistributionStat::StdDeviation
double StdDeviation() const
Definition: stats.cc:170
operations_research::TimeDistribution::AddTimeInCycles
void AddTimeInCycles(double cycles)
Definition: stats.cc:199
operations_research::DoubleDistribution::ValueAsString
std::string ValueAsString() const override
Definition: stats.cc:224
operations_research::RatioDistribution::Add
void Add(double value)
Definition: stats.cc:211
operations_research::DistributionStat
Definition: stats.h:175
operations_research::DoubleDistribution::DoubleDistribution
DoubleDistribution(const std::string &name, StatsGroup *group)
Definition: stats.h:276
operations_research::Stat::WorthPrinting
virtual bool WorthPrinting() const =0
name
const std::string name
Definition: default_search.cc:807
operations_research::DistributionStat::Max
double Max() const
Definition: stats.h:188
operations_research::Stat::~Stat
virtual ~Stat()
Definition: stats.h:99