#pragma once #include #include #include #include #include #include namespace NMonitoring { using TBucketBound = double; using TBucketValue = ui64; using TBucketBounds = TVector; using TBucketValues = TVector; constexpr ui32 HISTOGRAM_MAX_BUCKETS_COUNT = 51; constexpr TBucketBound HISTOGRAM_INF_BOUND = std::numeric_limits::max(); /////////////////////////////////////////////////////////////////////////// // IHistogramSnapshot /////////////////////////////////////////////////////////////////////////// class IHistogramSnapshot: public TAtomicRefCount { public: virtual ~IHistogramSnapshot() = default; /** * @return buckets count. */ virtual ui32 Count() const = 0; /** * @return upper bound for the bucket with particular index. */ virtual TBucketBound UpperBound(ui32 index) const = 0; /** * @return value stored in the bucket with particular index. */ virtual TBucketValue Value(ui32 index) const = 0; }; using IHistogramSnapshotPtr = TIntrusivePtr; /////////////////////////////////////////////////////////////////////////////// // TLinearHistogramSnapshot /////////////////////////////////////////////////////////////////////////////// class TLinearHistogramSnapshot: public IHistogramSnapshot { public: TLinearHistogramSnapshot( TBucketBound startValue, TBucketBound bucketWidth, TBucketValues values) : StartValue_(startValue) , BucketWidth_(bucketWidth) , Values_(std::move(values)) { } ui32 Count() const override { return static_cast(Values_.size()); } TBucketBound UpperBound(ui32 index) const override { Y_ASSERT(index < Values_.size()); if (index == Count() - 1) { return Max(); } return StartValue_ + BucketWidth_ * index; } TBucketValue Value(ui32 index) const override { Y_ASSERT(index < Values_.size()); return Values_[index]; } ui64 MemorySizeBytes() { return sizeof(*this) + Values_.capacity() * sizeof(decltype(Values_)::value_type); } private: TBucketBound StartValue_; TBucketBound BucketWidth_; TBucketValues Values_; }; /////////////////////////////////////////////////////////////////////////// // TExponentialHistogramSnapshot /////////////////////////////////////////////////////////////////////////// class TExponentialHistogramSnapshot: public IHistogramSnapshot { public: TExponentialHistogramSnapshot( double base, double scale, TBucketValues values) : Base_(base) , Scale_(scale) , Values_(std::move(values)) { } ui32 Count() const override { return static_cast(Values_.size()); } TBucketBound UpperBound(ui32 index) const override { Y_ASSERT(index < Values_.size()); if (index == Values_.size() - 1) { return Max(); } return std::round(Scale_ * std::pow(Base_, index)); } TBucketValue Value(ui32 index) const override { Y_ASSERT(index < Values_.size()); return Values_[index]; } ui64 MemorySizeBytes() { return sizeof(*this) + Values_.capacity() * sizeof(decltype(Values_)::value_type); } private: double Base_; double Scale_; TBucketValues Values_; }; using TBucket = std::pair; /////////////////////////////////////////////////////////////////////// // TExplicitHistogramSnapshot /////////////////////////////////////////////////////////////////////// // // Memory layout (single contiguous block): // // +------+-----------+--------------+--------+--------+- -+--------+--------+ // | vptr | RefsCount | BucketsCount | Bound1 | Value1 | ... | BoundN | ValueN | // +------+-----------+--------------+--------+--------+- -+--------+--------+ // class alignas(TBucketValue) TExplicitHistogramSnapshot: public IHistogramSnapshot, private TNonCopyable { public: static TIntrusivePtr New(ui32 bucketsCount) { size_t bucketsSize = bucketsCount * sizeof(TBucket); Y_ENSURE(bucketsCount <= HISTOGRAM_MAX_BUCKETS_COUNT, "Cannot allocate a histogram with " << bucketsCount << " buckets. Bucket count is limited to " << HISTOGRAM_MAX_BUCKETS_COUNT); return new(bucketsSize) TExplicitHistogramSnapshot(bucketsCount); } TBucket& operator[](ui32 index) noexcept { return Bucket(index); } ui32 Count() const override { return BucketsCount_; } TBucketBound UpperBound(ui32 index) const override { return Bucket(index).first; } TBucketValue Value(ui32 index) const override { return Bucket(index).second; } ui64 MemorySizeBytes() const { return sizeof(*this) + BucketsCount_ * sizeof(TBucket); } private: explicit TExplicitHistogramSnapshot(ui32 bucketsCount) noexcept : BucketsCount_(bucketsCount) { } static void* operator new(size_t size, size_t bucketsSize) { return ::operator new(size + bucketsSize); } static void operator delete(void* mem) { ::operator delete(mem); } static void operator delete(void* mem, size_t, size_t) { // this operator can be called as paired for custom new operator ::operator delete(mem); } TBucket& Bucket(ui32 index) noexcept { Y_DEBUG_ABORT_UNLESS(index < BucketsCount_); return *(reinterpret_cast(this + 1) + index); } const TBucket& Bucket(ui32 index) const noexcept { Y_DEBUG_ABORT_UNLESS(index < BucketsCount_); return *(reinterpret_cast(this + 1) + index); } private: ui32 BucketsCount_; }; static_assert(alignof(TExplicitHistogramSnapshot) == alignof(TBucket), "mismatched alingments of THistogramSnapshot and TBucket"); IHistogramSnapshotPtr ExplicitHistogramSnapshot(TConstArrayRef bounds, TConstArrayRef values, bool shrinkBuckets = false); } // namespace NMonitoring std::ostream& operator<<(std::ostream& os, const NMonitoring::IHistogramSnapshot& hist);