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 CAFFE2_API 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 
31 struct CAFFE2_API ExportedStatValue {
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 CAFFE2_API ExportedStatMap toMap(const ExportedStatList& stats);
44 
117 class CAFFE2_API StatRegistry {
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 CAFFE2_API 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 CAFFE2_API 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 
184 class CAFFE2_API AvgExportedStat : public ExportedStat {
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 
203 class CAFFE2_API StdDevExportedStat : public ExportedStat {
204  // Uses an offset (first_) to remove issue of cancellation
205  // Variance is then (sumsqoffset_ - (sumoffset_^2) / count_) / (count_ - 1)
206  private:
207  ExportedStat count_;
208  ExportedStat sumsqoffset_;
209  ExportedStat sumoffset_;
210  std::atomic<int64_t> first_{std::numeric_limits<int64_t>::min()};
211  int64_t const_min_{std::numeric_limits<int64_t>::min()};
212 
213  public:
214  StdDevExportedStat(const std::string& gn, const std::string& n)
215  : ExportedStat(gn, n + "/sum"),
216  count_(gn, n + "/count"),
217  sumsqoffset_(gn, n + "/sumsqoffset"),
218  sumoffset_(gn, n + "/sumoffset") {}
219 
220  int64_t increment(int64_t value = 1) {
221  first_.compare_exchange_strong(const_min_, value);
222  int64_t offset_value = first_.load();
223  int64_t orig_value = value;
224  value -= offset_value;
225  count_.increment();
226  sumsqoffset_.increment(value * value);
227  sumoffset_.increment(value);
228  return ExportedStat::increment(orig_value);
229  }
230 
231  template <typename T, typename Unused1, typename... Unused>
232  int64_t increment(T value, Unused1, Unused...) {
233  return increment(value);
234  }
235 };
236 
237 class CAFFE2_API DetailedExportedStat : public ExportedStat {
238  private:
239  std::vector<ExportedStat> details_;
240 
241  public:
242  DetailedExportedStat(const std::string& gn, const std::string& n)
243  : ExportedStat(gn, n) {}
244 
245  void setDetails(const std::vector<std::string>& detailNames) {
246  details_.clear();
247  for (const auto& detailName : detailNames) {
248  details_.emplace_back(groupName, name + "/" + detailName);
249  }
250  }
251 
252  template <typename T, typename... Unused>
253  int64_t increment(T value, size_t detailIndex, Unused...) {
254  if (detailIndex < details_.size()) {
255  details_[detailIndex].increment(value);
256  }
257  return ExportedStat::increment(value);
258  }
259 };
260 
261 class CAFFE2_API StaticStat : public Stat {
262  private:
263  StatValue* value_;
264 
265  public:
266  StaticStat(const std::string& groupName, const std::string& name)
267  : Stat(groupName, name),
268  value_(StatRegistry::get().add(groupName + "/" + name)) {}
269 
270  int64_t increment(int64_t value = 1) {
271  return value_->reset(value);
272  }
273 
274  template <typename T, typename Unused1, typename... Unused>
275  int64_t increment(T value, Unused1, Unused...) {
276  return increment(value);
277  }
278 };
279 
280 namespace detail {
281 
282 template <class T>
283 struct _ScopeGuard {
284  T f_;
285  std::chrono::high_resolution_clock::time_point start_;
286 
287  explicit _ScopeGuard(T f)
288  : f_(f), start_(std::chrono::high_resolution_clock::now()) {}
289  ~_ScopeGuard() {
290  using namespace std::chrono;
291  auto duration = high_resolution_clock::now() - start_;
292  int64_t nanos = duration_cast<nanoseconds>(duration).count();
293  f_(nanos);
294  }
295 
296  // Using implicit cast to bool so that it can be used in an 'if' condition
297  // within CAFFE_DURATION macro below.
298  /* implicit */ operator bool() {
299  return true;
300  }
301 };
302 
303 template <class T>
304 _ScopeGuard<T> ScopeGuard(T f) {
305  return _ScopeGuard<T>(f);
306 }
307 } // namespace detail
308 
309 #define CAFFE_STAT_CTOR(ClassName) \
310  ClassName(std::string name) : groupName(name) {} \
311  std::string groupName
312 
313 #define CAFFE_EXPORTED_STAT(name) \
314  ExportedStat name { \
315  groupName, #name \
316  }
317 
318 #define CAFFE_AVG_EXPORTED_STAT(name) \
319  AvgExportedStat name { \
320  groupName, #name \
321  }
322 
323 #define CAFFE_STDDEV_EXPORTED_STAT(name) \
324  StdDevExportedStat name { \
325  groupName, #name \
326  }
327 
328 #define CAFFE_DETAILED_EXPORTED_STAT(name) \
329  DetailedExportedStat name { \
330  groupName, #name \
331  }
332 
333 #define CAFFE_STAT(name) \
334  Stat name { \
335  groupName, #name \
336  }
337 
338 #define CAFFE_STATIC_STAT(name) \
339  StaticStat name { \
340  groupName, #name \
341  }
342 
343 #define CAFFE_EVENT(stats, field, ...) \
344  { \
345  auto __caffe_event_value_ = stats.field.increment(__VA_ARGS__); \
346  CAFFE_SDT( \
347  field, \
348  stats.field.groupName.c_str(), \
349  __caffe_event_value_, \
350  ##__VA_ARGS__); \
351  }
352 
353 #define CAFFE_DURATION(stats, field, ...) \
354  if (auto g = ::caffe2::detail::ScopeGuard([&](int64_t nanos) { \
355  CAFFE_EVENT(stats, field, nanos, ##__VA_ARGS__); \
356  }))
357 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
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
Holds a map of atomic counters keyed by name.
Definition: stats.h:117