Allocator Builder
Policy Based C++ Template Allocator Library
 All Classes Functions Variables Enumerations Enumerator Groups Pages
allocator_with_stats.hpp
1 //
3 // Copyright 2014 Felix Petriconi
4 //
5 // License: http://boost.org/LICENSE_1_0.txt, Boost License 1.0
6 //
7 // Authors:
8 // http://erdani.com, Andrei Alexandrescu
9 // Implementation in D from
10 // https://github.com/andralex/phobos/blob/allocator/std/allocator.d
11 // http://petriconi.net, Felix Petriconi
12 //
14 #pragma once
15 
16 #include "allocator_base.hpp"
17 #include "affix_allocator.hpp"
18 #include "internal/traits.hpp"
19 #include "internal/noatomic.hpp"
20 #include <atomic>
21 #include <chrono>
22 
23 namespace alb {
24 
26 #define ALLOCATE(A, N) A.allocate(N, __FILE__, __FUNCTION__, __LINE__)
27 
29 #define MEMBER_ACCESSOR(X) \
30 private: \
31  statistic_type X##_; \
32 public: \
33  size_t X() const noexcept { return X##_.load(); } \
34 
35 
36 inline namespace v_100 {
44 enum StatsOptions : unsigned {
48  NumOwns = 1u << 0,
53  NumAllocate = 1u << 1,
59  NumAllocateOK = 1u << 2,
64  NumExpand = 1u << 3,
70  NumExpandOK = 1u << 4,
75  NumReallocate = 1u << 5,
80  NumReallocateOK = 1u << 6,
94  NumDeallocate = 1u << 8,
98  NumDeallocateAll = 1u << 9,
102  NumAll = (1u << 10) - 1,
111  BytesAllocated = 1u << 10,
119  BytesDeallocated = 1u << 11,
124  BytesExpanded = 1u << 12,
129  BytesContracted = 1u << 13,
137  BytesMoved = 1u << 14,
143  BytesSlack = 1u << 15,
148  BytesHighTide = 1u << 16,
152  BytesAll = ((1u << 17) - 1) & ~NumAll,
158  CallerSize = 1u << 17,
163  CallerFile = 1u << 18,
168  CallerFunction = 1u << 19,
173  CallerLine = 1u << 20,
177  CallerTime = 1u << 21,
181  CallerAll = ((1u << 22) - 1) & ~NumAll & ~BytesAll,
185  All = (1u << 22) - 1
186 };
187 
208 template <bool Shared, class Allocator, unsigned Flags = alb::StatsOptions::All>
210 public:
217  struct AllocationInfo {
218  size_t callerSize;
219  const char *callerFile;
220  const char *callerFunction;
221  int callerLine;
222 
223  /* The comparison does not take the allocation time into account
224  * It is a template to be able to compare the AllocationInfo from different
225  * allocators
226  */
227  template <typename RHS>
228  bool operator==(const RHS &rhs) const {
229  return callerSize == rhs.callerSize &&
230  (callerFile == rhs.callerFile ||
231  ::strcmp(callerFile, rhs.callerFile) == 0) &&
232  (callerFunction == rhs.callerFunction ||
233  ::strcmp(callerFunction, rhs.callerFunction) == 0);
234  }
235 
236  std::chrono::time_point<std::chrono::system_clock> callerTime;
237  AllocationInfo *previous, *next;
238  };
239 
247  class Allocations {
248  public:
252  class iterator {
253  public:
254  using iterator_category = std::bidirectional_iterator_tag;
255  using value_type = AllocationInfo *;
256  using difference_type = ptrdiff_t;
257  using pointer = value_type;
258  using const_pointer = const pointer;
259  using reference = value_type;
260  using const_reference = const reference;
261 
262  iterator() : _node{nullptr} {}
263 
264  explicit iterator(AllocationInfo *data) : _node{data} {}
265 
266  reference operator*() const { return _node; }
267 
268  pointer operator->() const { return &(operator*()); }
269 
270  iterator &operator++() {
271  _node = _node->next;
272  return *this;
273  }
274 
275  iterator operator++(int) {
276  iterator tmp = *this;
277  ++*this;
278  return tmp;
279  }
280 
281  iterator &operator--() {
282  _node = _node->previous();
283  return *this;
284  }
285 
286  iterator operator--(int) {
287  iterator tmp = *this;
288  --*this;
289  return tmp;
290  }
291 
292  friend bool operator==(const iterator &x, const iterator &y) {
293  return x._node == y._node;
294  }
295 
296  friend bool operator!=(const iterator &x, const iterator &y) {
297  return !(x == y);
298  }
299 
300  private:
301  AllocationInfo *_node;
302  };
303 
304  public:
305  using const_iterator = const iterator;
306 
307  explicit Allocations(AllocationInfo *root) : _begin(root), _end(nullptr) {}
308 
309  const_iterator cbegin() const { return _begin; }
310 
311  const_iterator cend() const { return _end; }
312 
313  bool empty() const { return _begin == _end; }
314 
315  private:
316  const const_iterator _begin;
317  const const_iterator _end;
318  };
319 
320  static const bool HasPerAllocationState =
323 
324  static_assert(HasPerAllocationState && !Shared,
325  "Currently it is not supported to collect per file/line/time stats in shared mode!");
326 
327  using statistic_type = typename traits::type_switch<std::atomic<size_t>, internal::no_atomic<size_t>, Shared>::type;
328 
329 // Simplification for defining all members and accessors.
330 #define MEMBER_ACCESSORS \
331  MEMBER_ACCESSOR(num_owns) \
332  MEMBER_ACCESSOR(num_allocate) \
333  MEMBER_ACCESSOR(num_allocate_ok) \
334  MEMBER_ACCESSOR(num_expand) \
335  MEMBER_ACCESSOR(num_expand_ok) \
336  MEMBER_ACCESSOR(num_reallocate) \
337  MEMBER_ACCESSOR(num_reallocate_ok) \
338  MEMBER_ACCESSOR(num_reallocate_in_place) \
339  MEMBER_ACCESSOR(num_deallocate) \
340  MEMBER_ACCESSOR(num_deallocate_all) \
341  MEMBER_ACCESSOR(bytes_allocated) \
342  MEMBER_ACCESSOR(bytes_deallocated) \
343  MEMBER_ACCESSOR(bytes_expanded) \
344  MEMBER_ACCESSOR(bytes_contracted) \
345  MEMBER_ACCESSOR(bytes_moved) \
346  MEMBER_ACCESSOR(bytes_slack) \
347  MEMBER_ACCESSOR(bytes_high_tide)
348 
349  MEMBER_ACCESSORS
350 
351 #undef MEMBER_ACCESSOR
352 #undef MEMBER_ACCESSORS
353 
354  static constexpr bool supports_truncated_deallocation =
355  Allocator::supports_truncated_deallocation;
356  static constexpr bool has_per_allocation_state = HasPerAllocationState;
357  static constexpr unsigned alignment = Allocator::alignment;
358 
359  allocator_with_stats_base() noexcept : num_owns_(0),
360  num_allocate_(0),
361  num_allocate_ok_(0),
362  num_expand_(0),
363  num_expand_ok_(0),
364  num_reallocate_(0),
365  num_reallocate_ok_(0),
366  num_reallocate_in_place_(0),
367  num_deallocate_(0),
368  num_deallocate_all_(0),
369  bytes_allocated_(0),
370  bytes_deallocated_(0),
371  bytes_expanded_(0),
372  bytes_contracted_(0),
373  bytes_moved_(0),
374  bytes_slack_(0),
375  bytes_high_tide_(0),
376  root_(nullptr) {}
377 
390  block allocate(size_t n, const char *file = nullptr,
391  const char *function = nullptr, int line = 0) noexcept {
392 
393  auto result = allocator_.allocate(n);
394  up(StatsOptions::NumAllocate, num_allocate_);
395  upOK(StatsOptions::NumAllocateOK, num_allocate_ok_, n > 0 && result);
396  add(StatsOptions::BytesAllocated, bytes_allocated_, result.length);
397  update_high_tide();
398 
399  if (has_per_allocation_state) {
400  if (result) {
401  AllocationInfo *stat =
402  traits::affix_extractor<decltype(allocator_),
403  AllocationInfo>::prefix(allocator_, result);
404 
405  set(StatsOptions::CallerSize, stat->callerSize, n);
406  set(StatsOptions::CallerFile, stat->callerFile, file);
407  set(StatsOptions::CallerFunction, stat->callerFunction, function);
408  set(StatsOptions::CallerLine, stat->callerLine, line);
409  set(StatsOptions::CallerTime, stat->callerTime,
410  std::chrono::system_clock::now());
411 
412  // push into caller info stack
413  if (root_) {
414  root_->previous = stat;
415  stat->next = root_;
416  stat->previous = nullptr;
417  root_ = stat;
418  } else { // create the new stack
419  stat->previous = nullptr;
420  stat->next = nullptr;
421  root_ = stat;
422  }
423  }
424  }
425  return result;
426  }
427 
434  void deallocate(block &b) noexcept {
435  up(StatsOptions::NumDeallocate, num_deallocate_);
436  add(StatsOptions::BytesDeallocated, bytes_deallocated_, b.length);
437 
438  if (has_per_allocation_state) {
439  if (b) {
440  auto stat =
441  traits::affix_extractor<decltype(allocator_),
442  AllocationInfo>::prefix(allocator_, b);
443  if (stat->previous) {
444  stat->previous->next = stat->next;
445  }
446  if (stat->next) {
447  stat->next->previous = stat->previous;
448  }
449  if (stat == root_) {
450  root_ = stat->previous;
451  }
452  }
453  }
454  allocator_.deallocate(b);
455  }
456 
465  bool reallocate(block &b, size_t n) noexcept {
466  auto originalBlock = b;
467  auto wasRootBlock(false);
468  if (has_per_allocation_state) {
469  if (b) {
470  wasRootBlock =
471  root_ ==
472  traits::affix_extractor<decltype(allocator_),
473  AllocationInfo>::prefix(allocator_, b);
474  }
475  }
476  up(StatsOptions::NumReallocate, num_reallocate_);
477 
478  if (!allocator_.reallocate(b, n)) {
479  return false;
480  }
481  up(StatsOptions::NumReallocateOK, num_reallocate_ok_);
482  std::make_signed<size_t>::type delta = b.length - originalBlock.length;
483  if (b.ptr == originalBlock.ptr) {
484  up(StatsOptions::NumReallocateInPlace, num_reallocate_in_place_);
485  if (delta > 0) {
486  add(StatsOptions::BytesAllocated, bytes_allocated_, delta);
487  add(StatsOptions::BytesExpanded, bytes_expanded_, delta);
488  } else {
489  add(StatsOptions::BytesDeallocated, bytes_deallocated_, -delta);
490  add(StatsOptions::BytesContracted, bytes_contracted_, -delta);
491  }
492  } // was moved to a new location
493  else {
494  add(StatsOptions::BytesAllocated, bytes_allocated_, b.length);
495  add(StatsOptions::BytesMoved, bytes_moved_, originalBlock.length);
496  add(StatsOptions::BytesDeallocated, bytes_deallocated_,
497  originalBlock.length);
498 
499  if (has_per_allocation_state) {
500  if (b) {
501  auto stat =
502  traits::affix_extractor<decltype(allocator_),
503  AllocationInfo>::prefix(allocator_, b);
504  if (stat->next) {
505  stat->next->previous = stat;
506  }
507  if (stat->previous) {
508  stat->previous->next = stat;
509  }
510  if (wasRootBlock) {
511  root_ = stat;
512  }
513  }
514  }
515  }
516  update_high_tide();
517  return true;
518  }
519 
528  template <typename U = Allocator>
529  typename std::enable_if<traits::has_owns<U>::value, bool>::type
530  owns(const block &b) const noexcept {
531  up(StatsOptions::NumOwns, num_owns_);
532  return allocator_.owns(b);
533  }
534 
544  template <typename U = Allocator>
545  typename std::enable_if<traits::has_expand<U>::value, bool>::type
546  expand(block &b, size_t delta) noexcept {
547  up(StatsOptions::NumExpand, num_expand_);
548  auto oldLength = b.length;
549  auto result = allocator_.expand(b, delta);
550  if (result) {
551  up(StatsOptions::NumExpandOK, num_expand_ok_);
552  add(StatsOptions::BytesExpanded, bytes_expanded_, b.length - oldLength);
553  add(StatsOptions::BytesAllocated, bytes_allocated_, b.length - oldLength);
554  update_high_tide();
555  // if (b && has_per_allocation_state) {
556  // auto stat = traits::AffixExtractor<
557  // decltype(allocator_), AllocationInfo>::prefix(allocator_, b);
558  // }
559  }
560  return result;
561  }
562 
568  Allocations allocations() const noexcept { return Allocations(root_); }
569 
570 private:
574  template <typename T>
575  inline void up(StatsOptions option, T &value) const noexcept {
576  if (Flags & option)
577  ++value;
578  }
579 
584  template <typename T>
585  inline void upOK(StatsOptions option, T &value, bool ok) const noexcept {
586  if (Flags & option && ok)
587  ++value;
588  }
589 
594  template <typename T>
595  void inline add(StatsOptions option, T &value,
596  typename std::make_signed<typename T::type>::type delta) const noexcept {
597  if (Flags & option)
598  value += delta;
599  }
600 
604  template <typename T>
605  inline void set(StatsOptions option, T &value, T t) const noexcept {
606  if (Flags & option)
607  value = std::move(t);
608  }
609 
613  void update_high_tide() noexcept {
614  if (Flags & StatsOptions::BytesHighTide) {
615  const size_t currentlyAllocated = bytes_allocated_ - bytes_deallocated_;
616  if (bytes_high_tide_ < currentlyAllocated) {
617  bytes_high_tide_ = currentlyAllocated;
618  }
619  }
620  }
621 
626  typename traits::type_switch<affix_allocator<Allocator, AllocationInfo>,
627  Allocator,
628  HasPerAllocationState>::type allocator_;
629 
630  AllocationInfo *root_;
631 };
632 
633 
634 template <class Allocator, unsigned Flags = alb::StatsOptions::All>
635 class allocator_with_stats : public allocator_with_stats_base<false, Allocator, Flags>
636 {
637 public:
638  allocator_with_stats() noexcept {}
639 };
640 
641 // template <class Allocator, unsigned Flags = alb::StatsOptions::All>
642 // class shared_allocator_with_stats : public allocator_with_stats_base<true, Allocator, Flags>
643 // {
644 // public:
645 // shared_allocator_with_stats() noexcept {}
646 //};
647 
648 }
649 using namespace v_100;
650 }
Allocations allocations() const noexcept
std::enable_if< traits::has_owns< U >::value, bool >::type owns(const block &b) const noexcept
block allocate(size_t n, const char *file=nullptr, const char *function=nullptr, int line=0) noexcept
bool reallocate(block &b, size_t n) noexcept
std::enable_if< traits::has_expand< U >::value, bool >::type expand(block &b, size_t delta) noexcept