Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
stats.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 "ortools/util/stats.h"
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <memory>
20#include <string>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/strings/str_format.h"
25#include "absl/strings/string_view.h"
27#include "ortools/base/timer.h"
29
30namespace operations_research {
31
32std::string MemoryUsage() {
34 static const int64_t kDisplayThreshold = 2;
35 static const int64_t kKiloByte = 1024;
36 static const int64_t kMegaByte = kKiloByte * kKiloByte;
37 static const int64_t kGigaByte = kMegaByte * kKiloByte;
38 if (mem > kDisplayThreshold * kGigaByte) {
39 return absl::StrFormat("%.2lf GB", mem * 1.0 / kGigaByte);
40 } else if (mem > kDisplayThreshold * kMegaByte) {
41 return absl::StrFormat("%.2lf MB", mem * 1.0 / kMegaByte);
42 } else if (mem > kDisplayThreshold * kKiloByte) {
43 return absl::StrFormat("%2lf KB", mem * 1.0 / kKiloByte);
44 } else {
45 return absl::StrFormat("%d", mem);
46 }
47}
48
49Stat::Stat(absl::string_view name, StatsGroup* group) : name_(name) {
50 group->Register(this);
51}
52
53std::string Stat::StatString() const { return name_ + ": " + ValueAsString(); }
54
55void StatsGroup::Register(Stat* stat) { stats_.push_back(stat); }
56
58 for (int i = 0; i < stats_.size(); ++i) {
59 stats_[i]->Reset();
60 }
61}
62
63namespace {
64
65bool CompareStatPointers(const Stat* s1, const Stat* s2) {
66 if (s1->Priority() == s2->Priority()) {
67 if (s1->Sum() == s2->Sum()) return s1->Name() < s2->Name();
68 return (s1->Sum() > s2->Sum());
69 } else {
70 return (s1->Priority() > s2->Priority());
71 }
72}
73
74// This function counts the number of codepoints in the string and assumes well
75// formed UTF-8 strings. Codepoints can take up to 4 bytes.
76// * 1-byte codepoint : 0yyyzzzz
77// * 2-byte codepoint : 110xxxyy 10yyzzzz
78// * 3-byte codepoint : 1110wwww 10xxxxyy 10yyzzzz
79// * 4-byte codepoint : 11110uvv 10vvwwww 10xxxxyy 10yyzzzz
80// We count one codepoint for bytes where the two most significant bits are
81// different of 0b10, effectively discarding all trailing bytes in multibyte
82// codepoints.
83int UTF8StrLen(absl::string_view str) {
84 int len = 0;
85 for (const char c : str) {
86 if ((c & 0b11'00'0000) != 0b10'00'0000) {
87 ++len;
88 }
89 }
90 return len;
91}
92
93} // namespace
94
95std::string StatsGroup::StatString() const {
96 // Computes the longest name of all the stats we want to display.
97 // Also create a temporary vector so we can sort the stats by names.
98 int longest_name_size = 0;
99 std::vector<Stat*> sorted_stats;
100 for (int i = 0; i < stats_.size(); ++i) {
101 if (!stats_[i]->WorthPrinting()) continue;
102 // We support UTF8 characters in the stat names.
103 const int size = UTF8StrLen(stats_[i]->Name());
104 longest_name_size = std::max(longest_name_size, size);
105 sorted_stats.push_back(stats_[i]);
106 }
107 switch (print_order_) {
109 std::sort(sorted_stats.begin(), sorted_stats.end(), CompareStatPointers);
110 break;
111 case SORT_BY_NAME:
112 std::sort(sorted_stats.begin(), sorted_stats.end(),
113 [](const Stat* s1, const Stat* s2) -> bool {
114 return s1->Name() < s2->Name();
115 });
116 break;
117 default:
118 LOG(FATAL) << "Unknown print order: " << print_order_;
119 }
120
121 // Do not display groups without print-worthy stats.
122 if (sorted_stats.empty()) return "";
123
124 // Pretty-print all the stats.
125 std::string result(name_ + " {\n");
126 for (int i = 0; i < sorted_stats.size(); ++i) {
127 result += " ";
128 result += sorted_stats[i]->Name();
129 result.append(longest_name_size - UTF8StrLen(sorted_stats[i]->Name()), ' ');
130 result += " : " + sorted_stats[i]->ValueAsString();
131 }
132 result += "}\n";
133 return result;
134}
135
137 absl::string_view name) {
138 std::unique_ptr<TimeDistribution>& ref = time_distributions_[name];
139 if (ref == nullptr) {
140 ref = std::make_unique<TimeDistribution>(name);
141 Register(ref.get());
142 }
143 return ref.get();
144}
145
147 : Stat(name),
148 sum_(0.0),
149 average_(0.0),
151 min_(0.0),
152 max_(0.0),
153 num_(0) {}
154
155DistributionStat::DistributionStat(absl::string_view name, StatsGroup* group)
156 : Stat(name, group),
157 sum_(0.0),
158 average_(0.0),
160 min_(0.0),
161 max_(0.0),
162 num_(0) {}
163
165 sum_ = 0.0;
166 average_ = 0.0;
168 min_ = 0.0;
169 max_ = 0.0;
170 num_ = 0;
171}
172
174 if (num_ == 0) {
175 min_ = value;
176 max_ = value;
177 sum_ = value;
178 average_ = value;
179 num_ = 1;
180 return;
181 }
182 min_ = std::min(min_, value);
183 max_ = std::max(max_, value);
184 sum_ += value;
185 ++num_;
186 const double delta = value - average_;
187 average_ = sum_ / num_;
188 sum_squares_from_average_ += delta * (value - average_);
189}
190
191double DistributionStat::Average() const { return average_; }
192
194 if (num_ == 0) return 0.0;
195 return sqrt(sum_squares_from_average_ / num_);
196}
197
199 const double seconds_per_cycles = CycleTimerBase::CyclesToSeconds(1);
200 return cycles * seconds_per_cycles;
201}
202
203std::string TimeDistribution::PrintCyclesAsTime(double cycles) {
204 DCHECK_GE(cycles, 0.0);
205 // This epsilon is just to avoid displaying 1000.00ms instead of 1.00s.
206 double eps1 = 1 + 1e-3;
207 double sec = CyclesToSeconds(cycles);
208 if (sec * eps1 >= 3600.0) return absl::StrFormat("%.2fh", sec / 3600.0);
209 if (sec * eps1 >= 60.0) return absl::StrFormat("%.2fm", sec / 60.0);
210 if (sec * eps1 >= 1.0) return absl::StrFormat("%.2fs", sec);
211 if (sec * eps1 >= 1e-3) return absl::StrFormat("%.2fms", sec * 1e3);
212 if (sec * eps1 >= 1e-6) return absl::StrFormat("%.2fus", sec * 1e6);
213 return absl::StrFormat("%.2fns", sec * 1e9);
214}
215
216void TimeDistribution::AddTimeInSec(double seconds) {
217 DCHECK_GE(seconds, 0.0);
218 const double cycles_per_seconds = 1.0 / CycleTimerBase::CyclesToSeconds(1);
219 AddToDistribution(seconds * cycles_per_seconds);
220}
221
223 DCHECK_GE(cycles, 0.0);
224 AddToDistribution(cycles);
225}
226
228 return absl::StrFormat(
229 "%8u [%8s, %8s] %8s %8s %8s\n", num_, PrintCyclesAsTime(min_),
230 PrintCyclesAsTime(max_), PrintCyclesAsTime(Average()),
231 PrintCyclesAsTime(StdDeviation()), PrintCyclesAsTime(sum_));
232}
233
234void RatioDistribution::Add(double value) {
235 DCHECK_GE(value, 0.0);
236 AddToDistribution(value);
237}
238
240 return absl::StrFormat("%8u [%7.2f%%, %7.2f%%] %7.2f%% %7.2f%%\n", num_,
241 100.0 * min_, 100.0 * max_, 100.0 * Average(),
242 100.0 * StdDeviation());
243}
244
245void DoubleDistribution::Add(double value) { AddToDistribution(value); }
246
248 return absl::StrFormat("%8u [%8.1e, %8.1e] %8.1e %8.1e\n", num_, min_, max_,
249 Average(), StdDeviation());
250}
251
252void IntegerDistribution::Add(int64_t value) {
253 AddToDistribution(static_cast<double>(value));
254}
255
257 return absl::StrFormat("%8u [%8.f, %8.f] %8.2f %8.2f %8.f\n", num_, min_,
259}
260
261} // namespace operations_research
static double CyclesToSeconds(int64_t c)
Definition timer.h:90
void AddToDistribution(double value)
Definition stats.cc:173
std::string ValueAsString() const override
Definition stats.cc:247
std::string ValueAsString() const override
Definition stats.cc:256
std::string ValueAsString() const override
Definition stats.cc:239
std::string Name() const
Definition stats.h:100
virtual int Priority() const
Definition stats.h:109
virtual std::string ValueAsString() const =0
Stat(absl::string_view name)
Definition stats.h:93
std::string StatString() const
Definition stats.cc:53
virtual double Sum() const
Definition stats.h:113
TimeDistribution * LookupOrCreateTimeDistribution(absl::string_view name)
Definition stats.cc:136
std::string StatString() const
Definition stats.cc:95
void Register(Stat *stat)
Definition stats.cc:55
std::string ValueAsString() const override
Definition stats.cc:227
void AddTimeInSec(double seconds)
Definition stats.cc:216
static double CyclesToSeconds(double num_cycles)
Definition stats.cc:198
void AddTimeInCycles(double cycles)
Definition stats.cc:222
OR-Tools root namespace.
std::string MemoryUsage()
Definition stats.cc:32