Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
benchmarkutils.cc
Go to the documentation of this file.
1 
19 
20 #include <algorithm>
21 #include <iomanip>
22 #include <set>
23 
24 #include "ion/base/logging.h"
25 #include "ion/math/utils.h"
27 
28 namespace ion {
29 namespace analytics {
30 
32 
37 
38 
39 namespace {
40 
42 static const int kValueWidth = 12;
43 static const int kValuePrecision = 6;
44 static const double kTolerance(1e-6);
45 
47 struct Str {
48  Str(const std::string& str_in, int width_in) : str(str_in), width(width_in) {}
49  const std::string& str;
50  int width;
51 };
52 std::ostream& operator<<(std::ostream& out, const Str& s) {
53  return out << std::setw(s.width) << s.str;
54 }
55 
58 struct Double {
59  Double(double value_in, int width_in, int precision_in)
60  : value(value_in),
61  width(width_in),
62  precision(precision_in) {}
63  double value;
64  int width;
65  int precision;
66 };
67 std::ostream& operator<<(std::ostream& out, const Double& d) {
68  return out << std::right << std::setw(d.width)
69  << std::setprecision(d.precision) << d.value;
70 }
71 
74 template <typename T>
75 static const std::set<std::string> GetBenchmarkIdSet(
76  const std::vector<T>& items) {
77  std::set<std::string> ids;
78  const size_t count = items.size();
79  for (size_t i = 0; i < count; ++i)
80  ids.insert(items[i].descriptor.id);
81  return ids;
82 }
83 
85 template <typename T> static void AddItem(const T& item, Benchmark* b) {
86  DCHECK(false) << "Unspecialized AddItem() called";
87 }
88 template <> inline void AddItem(const Benchmark::Constant& item, Benchmark* b) {
89  b->AddConstant(item);
90 }
91 template <> inline void AddItem(const Benchmark::SampledVariable& item,
92  Benchmark* b) {
93  b->AddSampledVariable(item);
94 }
95 template <> inline void AddItem(const Benchmark::AccumulatedVariable& item,
96  Benchmark* b) {
97  b->AddAccumulatedVariable(item);
98 }
99 
102 template <typename T> static size_t MergeBenchmarkItems(
103  const std::string& item_type, const std::vector<T>& from_items,
104  std::set<std::string> ids, Benchmark* to) {
105  size_t num_conflicts = 0;
106  const size_t count = from_items.size();
107  for (size_t i = 0; i < count; ++i) {
108  const std::string& id = from_items[i].descriptor.id;
109  if (ids.count(id)) {
110  LOG(ERROR) << "Conflicting " << item_type << " \"" << id
111  << "\" found while merging benchmarks";
112  ++num_conflicts;
113  } else {
114  AddItem(from_items[i], to);
115  }
116  }
117  return num_conflicts;
118 }
119 
121 static void OutputConstantAsCsv(
122  const Benchmark::Constant& c, std::ostream& out) { // NOLINT
123  out << c.descriptor.id << ","
124  << c.descriptor.description << ","
125  << c.descriptor.group << ","
126  << c.value << ","
127  << c.descriptor.units << ","
128  << ",,," // No minimum, maximum, or standard deviation.
129  << std::endl;
130 }
131 
133 static void OutputAccumulatedVariableAsCsv(
134  const Benchmark::AccumulatedVariable& v, std::ostream& out) { // NOLINT
135  out << v.descriptor.id << ","
136  << v.descriptor.description << ","
137  << v.descriptor.group << ","
138  << v.mean << ","
139  << v.descriptor.units << ",";
140 
142  if (v.minimum != v.maximum) {
143  out << v.minimum << "," << v.maximum << ",";
144  } else {
145  out << ",,";
146  }
147 
149  if (v.standard_deviation != 0.0) {
150  out << v.standard_deviation << ","
151  << (100.0 * v.standard_deviation / v.mean);
152  } else {
153  out << ",";
154  }
155  out << std::endl;
156 }
157 
159 static void OutputKey(const std::string& type,
160  const Benchmark::Descriptor& descriptor,
161  int id_width,
162  std::ostream& out) { // NOLINT
163  out << " [" << type << "] " << std::right << Str(descriptor.id, id_width)
164  << ": " << descriptor.description;
165  if (!descriptor.units.empty())
166  out << " (" << descriptor.units << ")";
167  out << "\n";
168 }
169 
171 static void OutputConstantPretty(
172  const Benchmark::Constant& c,
173  int id_width,
174  int units_width,
175  std::ostream& out) { // NOLINT
176  out << Str(c.descriptor.id, id_width)
177  << Double(c.value, kValueWidth, kValuePrecision)
178  << Str(c.descriptor.units, units_width)
179  << "\n";
180 }
181 
183 static void OutputAccumulatedVariablePretty(
184  const Benchmark::AccumulatedVariable& v,
185  int id_width,
186  int units_width,
187  std::ostream& out) { // NOLINT
188  const double rel_stddev =
189  v.mean == 0.0 ? 0.0 : 100.0 * v.standard_deviation / v.mean;
190  out << Str(v.descriptor.id, id_width)
191  << Double(v.mean, kValueWidth, kValuePrecision)
192  << Str(v.descriptor.units, units_width)
193  << Double(v.minimum, kValueWidth, kValuePrecision)
194  << Double(v.maximum, kValueWidth, kValuePrecision)
195  << Double(rel_stddev, kValueWidth - 2, 4) << " %"
196  << "\n";
197 }
198 
199 } // anonymous namespace
200 
202 
207 
208 
209 size_t MergeBenchmarks(const Benchmark& from, Benchmark* to) {
210  if (!to)
211  return 0;
212 
214  const std::set<std::string> constant_ids =
215  GetBenchmarkIdSet(to->GetConstants());
216  const std::set<std::string> sampled_variable_ids =
217  GetBenchmarkIdSet(to->GetSampledVariables());
218  const std::set<std::string> accumulated_variable_ids =
219  GetBenchmarkIdSet(to->GetAccumulatedVariables());
220 
223  size_t num_conflicts =
224  MergeBenchmarkItems("Constant", from.GetConstants(), constant_ids, to);
225  num_conflicts += MergeBenchmarkItems(
226  "SampledVariable", from.GetSampledVariables(), sampled_variable_ids, to);
227  num_conflicts += MergeBenchmarkItems(
228  "AccumulatedVariable", from.GetAccumulatedVariables(),
229  accumulated_variable_ids, to);
230  return num_conflicts;
231 }
232 
233 void OutputBenchmarkAsCsv(const Benchmark& benchmark,
234  std::ostream& out) { // NOLINT
236  out << "Entry ID, Description, Group, Average, Units, Minimum, Maximum, "
237  << "Standard Deviation, Relative Deviation %\n";
238 
240  const std::vector<Benchmark::Constant>& constants = benchmark.GetConstants();
241  const size_t num_constants = constants.size();
242  for (size_t i = 0; i < num_constants; ++i)
243  OutputConstantAsCsv(constants[i], out);
244 
246  const std::vector<Benchmark::SampledVariable>& sampled_variables =
247  benchmark.GetSampledVariables();
248  const size_t num_sampled_variables = sampled_variables.size();
249  for (size_t i = 0; i < num_sampled_variables; ++i)
250  OutputAccumulatedVariableAsCsv(
251  Benchmark::AccumulateSampledVariable(sampled_variables[i]), out);
252 
254  const std::vector<Benchmark::AccumulatedVariable>& accumulated_variables =
255  benchmark.GetAccumulatedVariables();
256  const size_t num_accumulated_variables = accumulated_variables.size();
257  for (size_t i = 0; i < num_accumulated_variables; ++i)
258  OutputAccumulatedVariableAsCsv(accumulated_variables[i], out);
259 }
260 
262  const std::string& indent,
263  std::ostream& out) { // NOLINT
264  out << indent << "{" << std::endl;
265  out << indent << " \"id\": \"" << c.descriptor.id << "\"," << std::endl
266  << indent << " \"description\": \"" << c.descriptor.description << "\","
267  << std::endl
268  << indent << " \"group\": \"" << c.descriptor.group << "\"," << std::endl
269  << indent << " \"value\": " << c.value << "," << std::endl
270  << indent << " \"units\": \"" << c.descriptor.units << "\"" << std::endl;
271  out << indent << "}";
272 }
273 
275  const std::string& indent,
276  std::ostream& out) { // NOLINT
277  out << indent << "{" << std::endl;
278  out << indent << " \"id\": \"" << v.descriptor.id << "\"," << std::endl
279  << indent << " \"description\": \"" << v.descriptor.description << "\","
280  << std::endl
281  << indent << " \"group\": \"" << v.descriptor.group << "\"," << std::endl
282  << indent << " \"mean\": " << v.mean << "," << std::endl
283  << indent << " \"units\": \"" << v.descriptor.units << "\"";
284 
286  if (v.minimum != v.maximum) {
287  out << "," << std::endl;
288  out << indent << " \"minimum\": " << v.minimum << "," << std::endl
289  << indent << " \"maximum\": " << v.maximum;
290  }
291 
294  if (!isnan(v.standard_deviation) && !isinf(v.standard_deviation) &&
295  math::Abs(v.standard_deviation) > kTolerance &&
296  math::Abs(v.mean) > kTolerance) {
297  out << "," << std::endl;
298  out << indent << " \"standard_deviation\": " << v.standard_deviation << ","
299  << std::endl << indent
300  << " \"variation\": " << (100.0 * v.standard_deviation / v.mean);
301  }
302  out << std::endl << indent << "}";
303 }
304 
305 void OutputBenchmarkAsJson(const Benchmark& benchmark,
306  const std::string& indent_in,
307  std::ostream& out) { // NOLINT
308  const std::string indent = indent_in + std::string(" ");
309  out << indent_in << "{" << std::endl;
310 
312  const std::vector<Benchmark::Constant>& constants = benchmark.GetConstants();
313  const size_t num_constants = constants.size();
314  if (num_constants) {
315  out << indent_in << " \"constants\": [" << std::endl;
316  for (size_t i = 0; i < num_constants; ++i) {
317  OutputConstantAsJson(constants[i], indent, out);
318  if (i < num_constants - 1)
319  out << "," << std::endl;
320  }
321  out << std::endl << indent_in << " ]";
322  }
323 
325  const std::vector<Benchmark::SampledVariable>& sampled_variables =
326  benchmark.GetSampledVariables();
327  const size_t num_sampled_variables = sampled_variables.size();
328  if (num_sampled_variables) {
329  if (num_constants)
330  out << "," << std::endl;
331  out << indent_in << " \"sampled_variables\": [" << std::endl;
332  for (size_t i = 0; i < num_sampled_variables; ++i) {
334  Benchmark::AccumulateSampledVariable(sampled_variables[i]), indent,
335  out);
336  if (i < num_sampled_variables - 1)
337  out << "," << std::endl;
338  }
339  out << std::endl << indent_in << " ]";
340  }
341 
343  const std::vector<Benchmark::AccumulatedVariable>& accumulated_variables =
344  benchmark.GetAccumulatedVariables();
345  const size_t num_accumulated_variables = accumulated_variables.size();
346  if (num_accumulated_variables) {
347  if (num_constants || num_sampled_variables)
348  out << "," << std::endl;
349  out << indent_in << " \"accumulated_variables\": [" << std::endl;
350  for (size_t i = 0; i < num_accumulated_variables; ++i) {
351  OutputAccumulatedVariableAsJson(accumulated_variables[i], indent, out);
352  if (i < num_accumulated_variables - 1)
353  out << "," << std::endl;
354  }
355  out << std::endl << indent_in << " ]";
356  }
357 
358  out << std::endl << indent_in << "}" << std::endl;
359 }
360 
362 void OutputBenchmarkPretty(const std::string& id_string,
363  bool print_descriptions,
364  const Benchmark& benchmark,
365  std::ostream& out) { // NOLINT
366  static const char kSeparator[] = "----------------------------------------"
367  "---------------------------------------\n";
368 
370  out << kSeparator << "Benchmark report for \"" << id_string << "\"\n\n";
371 
372  const std::vector<Benchmark::Constant>& constants = benchmark.GetConstants();
373  const std::vector<Benchmark::SampledVariable>& sampled_variables =
374  benchmark.GetSampledVariables();
375  const std::vector<Benchmark::AccumulatedVariable>& accumulated_variables =
376  benchmark.GetAccumulatedVariables();
377  const size_t num_constants = constants.size();
378  const size_t num_sampled_variables = sampled_variables.size();
379  const size_t num_accumulated_variables = accumulated_variables.size();
380 
382  static const int kMinIdWidth = 2; // width of "ID"
383  size_t id_width_size_t = kMinIdWidth;
384  for (size_t i = 0; i < num_constants; ++i)
385  id_width_size_t = std::max(id_width_size_t,
386  constants[i].descriptor.id.length());
387  for (size_t i = 0; i < num_sampled_variables; ++i)
388  id_width_size_t = std::max(id_width_size_t,
389  sampled_variables[i].descriptor.id.length());
390  for (size_t i = 0; i < num_accumulated_variables; ++i)
391  id_width_size_t = std::max(id_width_size_t,
392  accumulated_variables[i].descriptor.id.length());
393  const int id_width = static_cast<int>(id_width_size_t);
394 
396  static const int kMinUnitsWidth = 5; // width of "UNITS"
397  size_t units_width_size_t = kMinUnitsWidth;
398  for (size_t i = 0; i < num_constants; ++i)
399  units_width_size_t = std::max(units_width_size_t,
400  constants[i].descriptor.units.length());
401  for (size_t i = 0; i < num_sampled_variables; ++i)
402  units_width_size_t = std::max(
403  units_width_size_t, sampled_variables[i].descriptor.units.length());
404  for (size_t i = 0; i < num_accumulated_variables; ++i)
405  units_width_size_t = std::max(
406  units_width_size_t, accumulated_variables[i].descriptor.units.length());
407  ++units_width_size_t; // Add one preceding space.
408  const int units_width = static_cast<int>(units_width_size_t);
409 
411  if (print_descriptions) {
412  for (size_t i = 0; i < num_constants; ++i)
413  OutputKey("Constant", constants[i].descriptor, id_width, out);
414  for (size_t i = 0; i < num_sampled_variables; ++i)
415  OutputKey("Variable", sampled_variables[i].descriptor, id_width, out);
416  for (size_t i = 0; i < num_accumulated_variables; ++i)
417  OutputKey("Variable", accumulated_variables[i].descriptor, id_width, out);
418 
419  out << kSeparator;
420  }
421 
423  out << Str("ID", id_width)
424  << Str("MEAN", kValueWidth)
425  << Str("UNITS", units_width)
426  << Str("MINIMUM", kValueWidth)
427  << Str("MAXIMUM", kValueWidth)
428  << Str("REL STDDEV", kValueWidth)
429  << "\n";
430 
432  for (size_t i = 0; i < num_constants; ++i)
433  OutputConstantPretty(constants[i], id_width, units_width, out);
434 
436  for (size_t i = 0; i < num_sampled_variables; ++i) {
437  OutputAccumulatedVariablePretty(Benchmark::AccumulateSampledVariable(
438  sampled_variables[i]), id_width, units_width, out);
439  }
440 
442  for (size_t i = 0; i < num_accumulated_variables; ++i)
443  OutputAccumulatedVariablePretty(
444  accumulated_variables[i], id_width, units_width, out);
445 
446  out << kSeparator << std::endl;
447 }
448 
449 } // namespace analytics
450 } // namespace ion
void OutputBenchmarkAsJson(const Benchmark &benchmark, const std::string &indent_in, std::ostream &out)
Outputs benchmark results as JSON, suitable for serialization and use in performance dashboards...
const std::string & str
void OutputBenchmarkAsCsv(const Benchmark &benchmark, std::ostream &out)
Outputs benchmark results as CSV (comma-separated values), suitable for use in performance dashboards...
std::string type
Definition: printer.cc:353
#define DCHECK(expr)
Definition: logging.h:331
double value
const std::vector< Constant > & GetConstants() const
Each of these returns the results for a given type of measurement.
Definition: benchmark.h:148
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Definition: logging.h:216
const std::vector< SampledVariable > & GetSampledVariables() const
Definition: benchmark.h:149
std::ostream & operator<<(std::ostream &os, const DateTime &dtime)
Definition: datetime.cc:361
void OutputConstantAsJson(const Benchmark::Constant &c, const std::string &indent, std::ostream &out)
Outputs a Constant as JSON. See below for the output format.
static const AccumulatedVariable AccumulateSampledVariable(const SampledVariable &sampled_variable)
Converts a SampledVariable to an AccumulatedVariable by accumulating all of the samples.
Definition: benchmark.cc:97
size_t MergeBenchmarks(const Benchmark &from, Benchmark *to)
Public functions.
This struct represents a number that is constant over all samples.
Definition: benchmark.h:54
const std::vector< AccumulatedVariable > & GetAccumulatedVariables() const
Definition: benchmark.h:152
void OutputBenchmarkPretty(const std::string &id_string, bool print_descriptions, const Benchmark &benchmark, std::ostream &out)
Outputs benchmark results in pretty format.
Copyright 2016 Google Inc.
void OutputAccumulatedVariableAsJson(const Benchmark::AccumulatedVariable &v, const std::string &indent, std::ostream &out)
Outputs an AccumulatedVariable as JSON. See below for the output format.
int width
The Benchmark class provides types and utilities to make it easier to create performance benchmarks...
Definition: benchmark.h:34
int precision
const T Abs(const T &val)
Returns the absolute value of a number in a type-safe way.
Definition: utils.h:42
This struct represents accumulated values for a variable.
Definition: benchmark.h:84