histogram_snapshot.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #pragma once
  2. #include <util/generic/array_ref.h>
  3. #include <util/generic/ptr.h>
  4. #include <util/generic/vector.h>
  5. #include <util/generic/yexception.h>
  6. #include <cmath>
  7. #include <limits>
  8. namespace NMonitoring {
  9. using TBucketBound = double;
  10. using TBucketValue = ui64;
  11. using TBucketBounds = TVector<TBucketBound>;
  12. using TBucketValues = TVector<TBucketValue>;
  13. constexpr ui32 HISTOGRAM_MAX_BUCKETS_COUNT = 51;
  14. constexpr TBucketBound HISTOGRAM_INF_BOUND = std::numeric_limits<TBucketBound>::max();
  15. ///////////////////////////////////////////////////////////////////////////
  16. // IHistogramSnapshot
  17. ///////////////////////////////////////////////////////////////////////////
  18. class IHistogramSnapshot: public TAtomicRefCount<IHistogramSnapshot> {
  19. public:
  20. virtual ~IHistogramSnapshot() = default;
  21. /**
  22. * @return buckets count.
  23. */
  24. virtual ui32 Count() const = 0;
  25. /**
  26. * @return upper bound for the bucket with particular index.
  27. */
  28. virtual TBucketBound UpperBound(ui32 index) const = 0;
  29. /**
  30. * @return value stored in the bucket with particular index.
  31. */
  32. virtual TBucketValue Value(ui32 index) const = 0;
  33. };
  34. using IHistogramSnapshotPtr = TIntrusivePtr<IHistogramSnapshot>;
  35. ///////////////////////////////////////////////////////////////////////////////
  36. // TLinearHistogramSnapshot
  37. ///////////////////////////////////////////////////////////////////////////////
  38. class TLinearHistogramSnapshot: public IHistogramSnapshot {
  39. public:
  40. TLinearHistogramSnapshot(
  41. TBucketBound startValue, TBucketBound bucketWidth, TBucketValues values)
  42. : StartValue_(startValue)
  43. , BucketWidth_(bucketWidth)
  44. , Values_(std::move(values))
  45. {
  46. }
  47. ui32 Count() const override {
  48. return static_cast<ui32>(Values_.size());
  49. }
  50. TBucketBound UpperBound(ui32 index) const override {
  51. Y_ASSERT(index < Values_.size());
  52. if (index == Count() - 1) {
  53. return Max<TBucketBound>();
  54. }
  55. return StartValue_ + BucketWidth_ * index;
  56. }
  57. TBucketValue Value(ui32 index) const override {
  58. Y_ASSERT(index < Values_.size());
  59. return Values_[index];
  60. }
  61. ui64 MemorySizeBytes() {
  62. return sizeof(*this) + Values_.capacity() * sizeof(decltype(Values_)::value_type);
  63. }
  64. private:
  65. TBucketBound StartValue_;
  66. TBucketBound BucketWidth_;
  67. TBucketValues Values_;
  68. };
  69. ///////////////////////////////////////////////////////////////////////////
  70. // TExponentialHistogramSnapshot
  71. ///////////////////////////////////////////////////////////////////////////
  72. class TExponentialHistogramSnapshot: public IHistogramSnapshot {
  73. public:
  74. TExponentialHistogramSnapshot(
  75. double base, double scale, TBucketValues values)
  76. : Base_(base)
  77. , Scale_(scale)
  78. , Values_(std::move(values))
  79. {
  80. }
  81. ui32 Count() const override {
  82. return static_cast<ui32>(Values_.size());
  83. }
  84. TBucketBound UpperBound(ui32 index) const override {
  85. Y_ASSERT(index < Values_.size());
  86. if (index == Values_.size() - 1) {
  87. return Max<TBucketBound>();
  88. }
  89. return std::round(Scale_ * std::pow(Base_, index));
  90. }
  91. TBucketValue Value(ui32 index) const override {
  92. Y_ASSERT(index < Values_.size());
  93. return Values_[index];
  94. }
  95. ui64 MemorySizeBytes() {
  96. return sizeof(*this) + Values_.capacity() * sizeof(decltype(Values_)::value_type);
  97. }
  98. private:
  99. double Base_;
  100. double Scale_;
  101. TBucketValues Values_;
  102. };
  103. using TBucket = std::pair<TBucketBound, TBucketValue>;
  104. ///////////////////////////////////////////////////////////////////////
  105. // TExplicitHistogramSnapshot
  106. ///////////////////////////////////////////////////////////////////////
  107. //
  108. // Memory layout (single contiguous block):
  109. //
  110. // +------+-----------+--------------+--------+--------+- -+--------+--------+
  111. // | vptr | RefsCount | BucketsCount | Bound1 | Value1 | ... | BoundN | ValueN |
  112. // +------+-----------+--------------+--------+--------+- -+--------+--------+
  113. //
  114. class alignas(TBucketValue) TExplicitHistogramSnapshot: public IHistogramSnapshot, private TNonCopyable {
  115. public:
  116. static TIntrusivePtr<TExplicitHistogramSnapshot> New(ui32 bucketsCount) {
  117. size_t bucketsSize = bucketsCount * sizeof(TBucket);
  118. Y_ENSURE(bucketsCount <= HISTOGRAM_MAX_BUCKETS_COUNT, "Cannot allocate a histogram with " << bucketsCount
  119. << " buckets. Bucket count is limited to " << HISTOGRAM_MAX_BUCKETS_COUNT);
  120. return new(bucketsSize) TExplicitHistogramSnapshot(bucketsCount);
  121. }
  122. TBucket& operator[](ui32 index) noexcept {
  123. return Bucket(index);
  124. }
  125. ui32 Count() const override {
  126. return BucketsCount_;
  127. }
  128. TBucketBound UpperBound(ui32 index) const override {
  129. return Bucket(index).first;
  130. }
  131. TBucketValue Value(ui32 index) const override {
  132. return Bucket(index).second;
  133. }
  134. ui64 MemorySizeBytes() const {
  135. return sizeof(*this) + BucketsCount_ * sizeof(TBucket);
  136. }
  137. private:
  138. explicit TExplicitHistogramSnapshot(ui32 bucketsCount) noexcept
  139. : BucketsCount_(bucketsCount)
  140. {
  141. }
  142. static void* operator new(size_t size, size_t bucketsSize) {
  143. return ::operator new(size + bucketsSize);
  144. }
  145. static void operator delete(void* mem) {
  146. ::operator delete(mem);
  147. }
  148. static void operator delete(void* mem, size_t, size_t) {
  149. // this operator can be called as paired for custom new operator
  150. ::operator delete(mem);
  151. }
  152. TBucket& Bucket(ui32 index) noexcept {
  153. Y_DEBUG_ABORT_UNLESS(index < BucketsCount_);
  154. return *(reinterpret_cast<TBucket*>(this + 1) + index);
  155. }
  156. const TBucket& Bucket(ui32 index) const noexcept {
  157. Y_DEBUG_ABORT_UNLESS(index < BucketsCount_);
  158. return *(reinterpret_cast<const TBucket*>(this + 1) + index);
  159. }
  160. private:
  161. ui32 BucketsCount_;
  162. };
  163. static_assert(alignof(TExplicitHistogramSnapshot) == alignof(TBucket),
  164. "mismatched alingments of THistogramSnapshot and TBucket");
  165. IHistogramSnapshotPtr ExplicitHistogramSnapshot(TConstArrayRef<TBucketBound> bounds, TConstArrayRef<TBucketValue> values, bool shrinkBuckets = false);
  166. } // namespace NMonitoring
  167. std::ostream& operator<<(std::ostream& os, const NMonitoring::IHistogramSnapshot& hist);