Caffe2 - C++ API
A deep learning, cross platform ML framework
stats.h
1 #pragma once
2 
3 #include <atomic>
4 #include <memory>
5 #include <mutex>
6 #include <string>
7 #include <unordered_map>
8 #include <vector>
9 #include "caffe2/core/logging.h"
10 #include "caffe2/core/static_tracepoint.h"
11 
12 namespace caffe2 {
13 
14 class StatValue {
15  std::atomic<int64_t> v_{0};
16 
17  public:
18  int64_t increment(int64_t inc) {
19  return v_ += inc;
20  }
21 
22  int64_t reset(int64_t value = 0) {
23  return v_.exchange(value);
24  }
25 
26  int64_t get() const {
27  return v_.load();
28  }
29 };
30 
32  std::string key;
33  int64_t value;
34  std::chrono::time_point<std::chrono::high_resolution_clock> ts;
35 };
36 
40 using ExportedStatList = std::vector<ExportedStatValue>;
41 using ExportedStatMap = std::unordered_map<std::string, int64_t>;
42 
43 ExportedStatMap toMap(const ExportedStatList& stats);
44 
118  std::mutex mutex_;
119  std::unordered_map<std::string, std::unique_ptr<StatValue>> stats_;
120 
121  public:
126  static StatRegistry& get();
127 
132  StatValue* add(const std::string& name);
133 
139  void publish(ExportedStatList& exported, bool reset = false);
140 
141  ExportedStatList publish(bool reset = false) {
142  ExportedStatList stats;
143  publish(stats, reset);
144  return stats;
145  }
146 
151  void update(const ExportedStatList& data);
152 
153  ~StatRegistry();
154 };
155 
156 struct Stat {
157  std::string groupName;
158  std::string name;
159  Stat(const std::string& gn, const std::string& n) : groupName(gn), name(n) {}
160 
161  template <typename... Unused>
162  int64_t increment(Unused...) {
163  return -1;
164  }
165 };
166 
167 class ExportedStat : public Stat {
168  StatValue* value_;
169 
170  public:
171  ExportedStat(const std::string& gn, const std::string& n)
172  : Stat(gn, n), value_(StatRegistry::get().add(gn + "/" + n)) {}
173 
174  int64_t increment(int64_t value = 1) {
175  return value_->increment(value);
176  }
177 
178  template <typename T, typename Unused1, typename... Unused>
179  int64_t increment(T value, Unused1, Unused...) {
180  return increment(value);
181  }
182 };
183 
185  private:
186  ExportedStat count_;
187 
188  public:
189  AvgExportedStat(const std::string& gn, const std::string& n)
190  : ExportedStat(gn, n + "/sum"), count_(gn, n + "/count") {}
191 
192  int64_t increment(int64_t value = 1) {
193  count_.increment();
194  return ExportedStat::increment(value);
195  }
196 
197  template <typename T, typename Unused1, typename... Unused>
198  int64_t increment(T value, Unused1, Unused...) {
199  return increment(value);
200  }
201 };
202 
204  private:
205  std::vector<ExportedStat> details_;
206 
207  public:
208  DetailedExportedStat(const std::string& gn, const std::string& n)
209  : ExportedStat(gn, n) {}
210 
211  void setDetails(const std::vector<std::string>& detailNames) {
212  details_.clear();
213  for (const auto& detailName : detailNames) {
214  details_.emplace_back(groupName, name + "/" + detailName);
215  }
216  }
217 
218  template <typename T, typename... Unused>
219  int64_t increment(T value, size_t detailIndex, Unused...) {
220  if (detailIndex < details_.size()) {
221  details_[detailIndex].increment(value);
222  }
223  return ExportedStat::increment(value);
224  }
225 };
226 
227 namespace detail {
228 
229 template <class T>
230 struct _ScopeGuard {
231  T f_;
232  std::chrono::high_resolution_clock::time_point start_;
233 
234  explicit _ScopeGuard(T f)
235  : f_(f), start_(std::chrono::high_resolution_clock::now()) {}
236  ~_ScopeGuard() {
237  using namespace std::chrono;
238  auto duration = high_resolution_clock::now() - start_;
239  int64_t nanos = duration_cast<nanoseconds>(duration).count();
240  f_(nanos);
241  }
242 
243  // Using implicit cast to bool so that it can be used in an 'if' condition
244  // within CAFFE_DURATION macro below.
245  /* implicit */ operator bool() {
246  return true;
247  }
248 };
249 
250 template <class T>
252  return _ScopeGuard<T>(f);
253 }
254 }
255 
256 #define CAFFE_STAT_CTOR(ClassName) \
257  ClassName(std::string name) : groupName(name) {} \
258  std::string groupName
259 
260 #define CAFFE_EXPORTED_STAT(name) \
261  ExportedStat name { \
262  groupName, #name \
263  }
264 
265 #define CAFFE_AVG_EXPORTED_STAT(name) \
266  AvgExportedStat name { \
267  groupName, #name \
268  }
269 
270 #define CAFFE_DETAILED_EXPORTED_STAT(name) \
271  DetailedExportedStat name { \
272  groupName, #name \
273  }
274 
275 #define CAFFE_STAT(name) \
276  Stat name { \
277  groupName, #name \
278  }
279 
280 #define CAFFE_EVENT(stats, field, ...) \
281  { \
282  auto __caffe_event_value_ = stats.field.increment(__VA_ARGS__); \
283  CAFFE_SDT( \
284  field, \
285  stats.field.groupName.c_str(), \
286  __caffe_event_value_, \
287  ##__VA_ARGS__); \
288  }
289 
290 #define CAFFE_DURATION(stats, field, ...) \
291  if (auto g = detail::ScopeGuard([&](int64_t nanos) { \
292  CAFFE_EVENT(stats, field, nanos, ##__VA_ARGS__); \
293  }))
294 }
Holds a map of atomic counters keyed by name.
Definition: stats.h:117
Simple registry implementation in Caffe2 that uses static variables to register object creators durin...
ScopeGuardImplBase && ScopeGuard
This is largely unneeded if you just use auto for your guards.
Definition: scope_guard.h:158
std::vector< ExportedStatValue > ExportedStatList
Holds names and values of counters exported from a StatRegistry.
Definition: stats.h:40
static StatRegistry & get()
Retrieve the singleton StatRegistry, which gets populated through the CAFFE_EVENT macro...
Definition: stats.cc:49