cpp-common
stats.hh
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 2; -*-
2 
3 #pragma once
4 
11 #include "nvsl/string.hh"
12 #include "nvsl/error.hh"
13 
14 #include <concepts>
15 #include <cstddef>
16 #include <map>
17 #include <numeric>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21 
22 namespace nvsl {
23  template <class T>
24  concept Integral = std::is_integral<T>::value;
25 
26  template <typename T, typename I>
27  concept Averageable = Integral<I> && requires(T a, T b, I c) {
28  (a + b) / c;
29  };
30 
32  class StatsBase;
34  public:
35  static std::vector<StatsBase *> stats;
36 
37  ~StatsCollection();
38  };
39 
41  class StatsBase {
42  protected:
43  std::string stat_name;
44  std::string stat_desc;
45 
46  public:
47  StatsBase(bool reg) {
48  if (reg) {
49 #ifdef NVSL_ENABLE_COLLECTION_REGISTRATION
50  StatsCollection::stats.push_back(this);
51 #endif
52  }
53  };
54 
55  void init(const std::string &name, const std::string &desc) {
56  this->stat_name = name;
57  printf("Stat name = %s\n", this->stat_name.c_str());
58  this->stat_desc = desc;
59  }
60 
61  virtual double avg() const { return 0; }
62  virtual std::string str() const { return ""; }
63  virtual std::string latex(const std::string &prefix = "") const {
64  (void)prefix;
65  return "";
66  }
67  };
68 
73  template <typename T = size_t>
74  class StatsFreq : public StatsBase {
75  private:
76  size_t bucket_cnt;
77  T bucket_min, bucket_max, bucket_sz;
78  size_t *counts, underflow_cnt, overflow_cnt;
79 
80  public:
81  StatsFreq(bool reg = true) : StatsBase(reg) {};
82 
83  ~StatsFreq() {
84  delete[] counts;
85  }
86 
96  void init(const std::string &name, const std::string &desc,
97  size_t bucket_cnt, T bucket_min, T bucket_max) {
98 #if defined(DBGE)
99  NVSL_ASSERT(bucket_cnt != 0, "Bucket size cannot be zero");
100  NVSL_ASSERT(bucket_max > bucket_min,
101  "Bucket max cannot be smaller than bucket min");
102 #else
103  assert(bucket_cnt != 0 && "Bucket size cannot be zero");
104  assert(bucket_max > bucket_min &&
105  "Bucket max cannot be smaller than bucket min");
106 #endif // DBGE
107 
108  StatsBase::init(name, desc);
109 
110  this->bucket_cnt = bucket_cnt;
111  this->bucket_min = bucket_min;
112  this->bucket_max = bucket_max;
113  this->bucket_sz = (bucket_max-bucket_min)/bucket_cnt;
114  this->underflow_cnt = 0;
115  this->overflow_cnt = 0;
116 
117  this->counts = new size_t[bucket_cnt];
118  memset(counts, 0, sizeof(counts[0])*bucket_cnt);
119  }
120 
126  void add(T val, size_t count = 1) {
127  if (val < bucket_min) {
128  underflow_cnt++;
129  } else if (val >= bucket_max) {
130  overflow_cnt++;
131  } else {
132  size_t bucket_idx = (val-bucket_min)/bucket_sz;
133  counts[bucket_idx]++;
134  }
135  }
136 
141  size_t total() const {
142  return underflow_cnt + overflow_cnt +
143  std::accumulate(counts, counts+bucket_cnt, 0);
144  }
145 
151  size_t bucket_count(size_t bucket) const {
152  return counts[bucket];
153  }
154 
161  size_t uoflow_count(bool underflow_cnt, bool overflow_cnt) const {
162  return (underflow_cnt ? this->underflow_cnt : 0) +
163  (overflow_cnt ? this->overflow_cnt : 0);
164  }
165 
169  std::string str() const {
170  std::stringstream ss;
171  ss << stat_name + ".bucket_count: " << bucket_cnt << "\t# " + stat_desc << "\n"
172  << stat_name + ".bucket_min: " << bucket_min << "\t# " + stat_desc << "\n"
173  << stat_name + ".bucket_max: " << bucket_max << "\t# " + stat_desc << "\n"
174  << stat_name + ".bucket_size: " << bucket_sz << "\t# " + stat_desc << "\n"
175  << stat_name + ".underflow_count: " << underflow_cnt << "\t# " + stat_desc << "\n"
176  << stat_name + ".overflow_count: " << overflow_cnt << "\t# " + stat_desc << "\n";
177 
178  for (size_t i = 0; i < bucket_cnt; i++) {
179  ss << stat_name + ".bucket[" << i << "]: " << counts[i] << std::endl;
180  }
181 
182  return ss.str();
183  }
184  };
185 
187  class Counter : public StatsBase {
188  private:
189  size_t counter;
190  public:
191  Counter(bool reg = true) : StatsBase(reg), counter(0) {
192  printf("Counter constructed\n");
193  };
194 
195  void init(const std::string &name, const std::string &desc) {
196  StatsBase::init(name, desc);
197  }
198 
199  Counter& operator++() {
200  this->counter++;
201  return *this;
202  }
203 
204  Counter operator++(int) {
205  Counter result = *this;
206  ++this->counter;
207 
208  return result;
209  }
210 
211  size_t value() const {
212  return this->counter;
213  }
214 
216  std::string str() const override {
217  std::stringstream ss;
218  ss << StatsBase::stat_name << " = " << value();
219 
220  if (stat_desc != "") {
221  ss << " # " << stat_desc;
222  }
223 
224  return ss.str();
225  }
226  };
227 
229  class StatsScalar : public StatsBase {
230  private:
231  double total;
232  size_t count;
233 
234  bool is_time;
235  time_unit unit;
236 
237  public:
238  StatsScalar(bool reg = true) : StatsBase(reg), total(0), count(0){};
239 
240  void init(const std::string &name, const std::string &desc,
241  bool is_time = false, time_unit unit = time_unit::any_unit) {
242  StatsBase::init(name, desc);
243  this->is_time = is_time;
244  this->unit = unit;
245  }
246 
247  friend StatsScalar operator+(StatsScalar lhs, const auto rhs) {
248  lhs.total += rhs;
249  lhs.count++;
250  return lhs;
251  }
252 
253  StatsScalar &operator+=(const auto rhs) {
254  this->total += rhs;
255  this->count++;
256  return *this;
257  }
258 
260  double avg() const override { return total / (double)count; }
261 
263  std::string str() const override {
264  std::stringstream ss;
265  ss << stat_name << " = " << avg();
266 
267  if (this->is_time) {
268  ss << " (" << ns_to_hr(this->avg()) << ")";
269  }
270 
271  if (stat_desc != "") {
272  ss << " # " << stat_desc;
273  }
274 
275  return ss.str();
276  }
277 
278  std::string latex(const std::string &prefix = "") const override {
279  std::string name = "stat" + prefix + this->stat_name;
280  std::string result = "";
281  size_t ns, us, ms, s;
282 
283  name = nvsl::zip(nvsl::split(name, "_"), "");
284 
285  ns = this->avg();
286  us = this->avg() / 1000;
287  ms = this->avg() / 1000000;
288  s = this->avg() / 1000000000;
289 
290  switch (unit) {
291  case nvsl::time_unit::s_unit:
292  result = to_latex(name, ns, "~s", 1000000000);
293  break;
294  case nvsl::time_unit::ms_unit:
295  result = to_latex(name, ns, "~ms", 1000000);
296  break;
297  case nvsl::time_unit::us_unit:
298  result = to_latex(name, ns, "~\\us{}", 1000);
299  break;
300  case nvsl::time_unit::ns_unit:
301  result = to_latex(name, ns, "~ns", 1);
302  break;
303  case nvsl::time_unit::any_unit:
304  if (s != 0)
305  result = to_latex(name, ns, "~s", 1000000000);
306  else if (ms != 0)
307  result = to_latex(name, ns, "~ms", 1000000);
308  else if (us != 0)
309  result = to_latex(name, ns, "~\\us{}", 1000);
310  else
311  result = to_latex(name, ns, "~ns", 1);
312  break;
313  }
314 
315  result = result + " % total ops = " + std::to_string(this->count);
316 
317  return result;
318  };
319  };
320 
322  class StatsNamedVector : public StatsBase {
323  private:
324  std::map<std::string, StatsScalar> vec;
325  time_unit unit;
326 
327  public:
328  StatsNamedVector(bool reg = true) : StatsBase(reg){};
329 
330  void init(const std::string &name, const std::string &desc,
331  time_unit unit = time_unit::any_unit) {
332  StatsBase::init(name, desc);
333  this->unit = unit;
334  }
335 
336  StatsScalar &operator[](const std::string &memb_name) {
337  const auto memb = this->vec.find(memb_name);
338  const auto exists = memb != this->vec.end();
339 
340  if (not exists) {
341  vec.emplace(std::make_pair(memb_name, false));
342  vec[memb_name].init(memb_name, "", false, this->unit);
343  }
344 
345  return vec[memb_name];
346  }
347 
349  std::string str() const override {
350  std::stringstream ss;
351 
352  for (const auto &[k, v] : this->vec) {
353  ss << this->stat_name << "." << k << " = " << v.avg() << std::endl;
354  }
355 
356  return ss.str();
357  }
358 
359  std::string latex(const std::string &prefix = "") const override {
360  std::stringstream ss;
361 
362  for (const auto &[k, v] : this->vec) {
363  ss << v.latex(prefix + this->stat_name) << std::endl;
364  }
365 
366  return ss.str();
367  }
368  };
369 
370  inline StatsCollection::~StatsCollection() {
371  if (get_env_val(NVSL_GEN_STATS_ENV)) {
372  std::cout << std::endl << "==== Stats ====" << std::endl;
373  for (const auto stat : StatsCollection::stats) {
374  std::cout << stat->str() << std::endl;
375  std::cerr << stat->latex() << std::endl;
376  }
377  }
378  }
379 } // namespace nvsl
nvsl::zip
auto zip(const std::vector< std::string > arr, const std::string join_str)
Concat all the elements of a string vector into a stingle string.
Definition: string.hh:42
nvsl::StatsFreq::str
std::string str() const
Generate a string representation of the frequency map.
Definition: stats.hh:169
nvsl::Counter::str
std::string str() const override
Get the string representation of the stat.
Definition: stats.hh:216
nvsl::StatsNamedVector::str
std::string str() const override
Get the string representation of the stat.
Definition: stats.hh:349
error.hh
Brief description here.
nvsl::StatsFreq
Stat to measure freq of elements with a name and a description.
Definition: stats.hh:74
nvsl::StatsFreq::add
void add(T val, size_t count=1)
Add a value to the frequency map.
Definition: stats.hh:126
nvsl::Counter
Counts operations.
Definition: stats.hh:187
nvsl::StatsFreq::uoflow_count
size_t uoflow_count(bool underflow_cnt, bool overflow_cnt) const
Get the number of samples in overflow and underflow buckets.
Definition: stats.hh:161
nvsl::StatsFreq::init
void init(const std::string &name, const std::string &desc, size_t bucket_cnt, T bucket_min, T bucket_max)
Initialize the stats' buckets.
Definition: stats.hh:96
nvsl::StatsScalar::avg
double avg() const override
Get the average value per operation.
Definition: stats.hh:260
nvsl::StatsFreq::total
size_t total() const
Get the total number of samples.
Definition: stats.hh:141
nvsl::StatsBase
Base class for Stats.
Definition: stats.hh:41
nvsl::StatsFreq::bucket_count
size_t bucket_count(size_t bucket) const
Get the number of samples in a bucket.
Definition: stats.hh:151
NVSL_ASSERT
#define NVSL_ASSERT(cond, msg)
Assert a condition w/ msg and generate backtrace on fail.
Definition: error.hh:73
nvsl::StatsCollection
Definition: stats.hh:33
string.hh
Usefult string functions. Mostly resemble python's.
nvsl::StatsScalar::str
std::string str() const override
Get the string representation of the stat.
Definition: stats.hh:263
nvsl::StatsScalar
Represents a single stat with a name and a description.
Definition: stats.hh:229
nvsl::StatsNamedVector
Represents a vector of stats, each with a name.
Definition: stats.hh:322