cpp-common
clock.hh
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 2; -*-
2 
3 #pragma once
4 
11 #ifndef NVSL_ASSERT
12 #include <cassert>
13 #endif
14 
15 #include <algorithm>
16 #include <chrono>
17 #include <iostream>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21 
22 namespace nvsl {
23  inline std::string ns_to_hr_clk(const size_t ns_total) {
24  std::stringstream ss;
25 
26  const size_t s = ns_total / (1000000000);
27  const size_t ms = (ns_total - s * 1000000000) / (1000000);
28  const size_t us = (ns_total - s * 1000000000 - ms * 1000000) / (1000);
29  const size_t ns =
30  (ns_total - s * 1000000000 - ms * 1000000 - us * 1000);
31 
32  ss << s << "s " << ms << "ms " << us << "us " << ns << "ns";
33 
34  return ss.str();
35  }
36 
38  class Clock {
39  private:
40  std::chrono::system_clock::time_point start_clk;
41  bool running = false;
42  size_t total_ns = 0;
43  std::vector<size_t> raw_values;
44  std::vector<size_t> sorted_raw_values;
45 
47  static constexpr size_t RAW_VAL_CNT = 1024*1024*100;
48  public:
49  Clock() {
50  raw_values.reserve(RAW_VAL_CNT);
51  }
52 
54  void tick() {
55  running = true;
56  this->start_clk = std::chrono::high_resolution_clock::now();
57  }
58 
60  void tock() {
61  using namespace std::chrono;
62 
63  const auto end_clk = high_resolution_clock::now();
64  if (!running) {
65 #if defined(NVSL_ERROR) && defined(DBGE)
66  NVSL_ERROR("Clock not running");
67 #else
68  std::cerr << "Clock not running" << std::endl;
69  exit(1);
70 #endif
71  }
72 
73  running = false;
74  const auto elapsed = duration_cast<nanoseconds>(end_clk - start_clk).count();
75  this->total_ns += elapsed;
76 
77 #define OVERFLOW_MSG "Raw values buffer overflow, increase array size or " \
78  "operations/loop"
79 #if defined(NVSL_ASSERT) && defined(DBGE)
80  NVSL_ASSERT(this->raw_values.size() < RAW_VAL_CNT, OVERFLOW_MSG);
81 #else
82  if (this->raw_values.size() > RAW_VAL_CNT) {
83  fprintf(stderr, OVERFLOW_MSG "\n");
84  exit(1);
85  }
86 #endif
87  this->raw_values.push_back(elapsed);
88  }
89 
90  void reset() {
91  this->running = false;
92  this->total_ns = 0;
93  }
94 
95  size_t ns() const { return this->total_ns; }
96 
98  const std::string summarize() const {
99  std::stringstream ss;
100 
101  size_t ns_total = this->ns();
102 
103  ss << "Total ns: " << this->ns() << std::endl;
104  ss << "Total time: " << ns_to_hr_clk(ns_total);
105 
106  return ss.str();
107  }
108 
114  void reconcile() {
115  sorted_raw_values.reserve(raw_values.size());
116  std::copy(raw_values.begin(), raw_values.end(),
117  std::back_inserter(sorted_raw_values));
118  std::sort(sorted_raw_values.begin(), sorted_raw_values.end());
119  }
120 
125  size_t percentile(const size_t pc) const {
126  if (sorted_raw_values.size() == 0) {
127 #if defined(NVSL_ERROR)
128  NVSL_ERROR("Clock not reconcile. Call reconcile()");
129 #else
130  assert(0 && "Clock not reconciled. Call reconcile()");
131 #endif
132  }
133 
134  const auto sz = sorted_raw_values.size();
135  const auto idx = std::max(0UL, (size_t)((sz*pc)/100.0)-1);
136 
137  return sorted_raw_values[idx];
138  }
139 
145  std::string summarize(size_t total_ops, bool distribution = false) const {
146  std::stringstream ss;
147 
148 #ifdef NVSL_ASSERT
149  NVSL_ASSERT(total_ops != 0, "total ops cannot be zero");
150 #else
151  assert(total_ops != 0 && "Total ops cannot be zero");
152 #endif
153 
154  size_t ops_per_iter = total_ops/raw_values.size();
155  ss << this->summarize() << std::endl
156  << "ops/s: " << (total_ops * (1000000000)) / ((double)this->ns())
157  << "\n"
158  << "time/op: "
159  << ns_to_hr_clk((size_t)(this->ns() / (double)total_ops))
160  << "\np50/op: "
161  << ns_to_hr_clk((size_t)(this->percentile(50))/ops_per_iter)
162  << "\np90/op: "
163  << ns_to_hr_clk((size_t)(this->percentile(90))/ops_per_iter)
164  << "\np99/op: "
165  << ns_to_hr_clk((size_t)(this->percentile(99))/ops_per_iter)
166  << "\ntime/op: "
167  << ns_to_hr_clk((size_t)(this->ns() / (double)total_ops));
168 
169  return ss.str();
170  }
171 
172  size_t ns_per_op(size_t total_ops) const { return this->ns() / total_ops; }
173 
174  size_t percentile_per_op(const size_t total_ops, const size_t pc) const {
175  const auto ops_per_iter = total_ops / raw_values.size();
176  return this->percentile(pc)/ops_per_iter;
177  }
178  };
179 } // namespace nvsl
nvsl::Clock::summarize
std::string summarize(size_t total_ops, bool distribution=false) const
Summary with operations per second.
Definition: clock.hh:145
nvsl::Clock::tock
void tock()
Stop the timer.
Definition: clock.hh:60
nvsl::Clock::summarize
const std::string summarize() const
Total time elapsed.
Definition: clock.hh:98
nvsl::Clock::percentile
size_t percentile(const size_t pc) const
Calculate the percentile value.
Definition: clock.hh:125
NVSL_ERROR
#define NVSL_ERROR(msg)
Throw exception with msg if val is NULL.
Definition: error.hh:53
nvsl::Clock::sorted_raw_values
std::vector< size_t > sorted_raw_values
Definition: clock.hh:44
nvsl::Clock
Clock object based on std::chrono::high_resolution_clock.
Definition: clock.hh:38
nvsl::Clock::reconcile
void reconcile()
Prepare clock object to calculate percentile/summary.
Definition: clock.hh:114
NVSL_ASSERT
#define NVSL_ASSERT(cond, msg)
Assert a condition w/ msg and generate backtrace on fail.
Definition: error.hh:73
nvsl::Clock::tick
void tick()
Start the timer.
Definition: clock.hh:54