diff --git a/velox/common/base/Counters.cpp b/velox/common/base/Counters.cpp index 1389f689a0c9..00a503b8a5e2 100644 --- a/velox/common/base/Counters.cpp +++ b/velox/common/base/Counters.cpp @@ -143,6 +143,26 @@ void registerVeloxMetrics() { DEFINE_METRIC( kMetricMemoryPoolReservationLeakBytes, facebook::velox::StatType::SUM); + // The distribution of a root memory pool's initial capacity in range of [0, + // 256MB] with 32 buckets. It is configured to report the capacity at P50, + // P90, P99, and P100 percentiles. + + DEFINE_HISTOGRAM_METRIC( + kMetricMemoryPoolInitialCapacityBytes, + 8L << 20, + 0, + 256L << 20, + 50, + 90, + 99, + 100); + + // The distribution of a root memory pool cappacity growth attempts through + // memory arbitration in range of [0, 256] with 32 buckets. It is configured + // to report the count at P50, P90, P99, and P100 percentiles. + DEFINE_HISTOGRAM_METRIC( + kMetricMemoryPoolCapacityGrowCount, 8, 0, 256, 50, 90, 99, 100); + /// ================== Spill related Counters ================= // The number of bytes in memory to spill. diff --git a/velox/common/base/Counters.h b/velox/common/base/Counters.h index 1ad847206331..c78ea5e2afe6 100644 --- a/velox/common/base/Counters.h +++ b/velox/common/base/Counters.h @@ -55,6 +55,12 @@ constexpr folly::StringPiece kMetricTaskMemoryReclaimWaitTimeoutCount{ constexpr folly::StringPiece kMetricMemoryNonReclaimableCount{ "velox.memory_non_reclaimable_count"}; +constexpr folly::StringPiece kMetricMemoryPoolInitialCapacityBytes{ + "velox.memory_pool_initial_capacity_bytes"}; + +constexpr folly::StringPiece kMetricMemoryPoolCapacityGrowCount{ + "velox.memory_pool_capacity_growth_count"}; + constexpr folly::StringPiece kMetricMemoryPoolUsageLeakBytes{ "velox.memory_pool_usage_leak_bytes"}; diff --git a/velox/common/memory/Memory.cpp b/velox/common/memory/Memory.cpp index f3588b266c54..6b89ddbf1512 100644 --- a/velox/common/memory/Memory.cpp +++ b/velox/common/memory/Memory.cpp @@ -15,6 +15,9 @@ */ #include "velox/common/memory/Memory.h" + +#include "velox/common/base/Counters.h" +#include "velox/common/base/StatsReporter.h" #include "velox/common/memory/MallocAllocator.h" #include "velox/common/memory/MmapAllocator.h" @@ -207,6 +210,8 @@ std::shared_ptr MemoryManager::addRootPool( VELOX_CHECK_EQ(pool->capacity(), 0); arbitrator_->growCapacity( pool.get(), std::min(poolInitCapacity_, maxCapacity)); + RECORD_HISTOGRAM_METRIC_VALUE( + kMetricMemoryPoolInitialCapacityBytes, pool->capacity()); return pool; } diff --git a/velox/common/memory/MemoryPool.cpp b/velox/common/memory/MemoryPool.cpp index 5e854c5bcdd2..5b99577d8b05 100644 --- a/velox/common/memory/MemoryPool.cpp +++ b/velox/common/memory/MemoryPool.cpp @@ -165,7 +165,7 @@ std::string capacityToString(int64_t capacity) { std::string MemoryPool::Stats::toString() const { return fmt::format( - "currentBytes:{} reservedBytes:{} peakBytes:{} cumulativeBytes:{} numAllocs:{} numFrees:{} numReserves:{} numReleases:{} numShrinks:{} numReclaims:{} numCollisions:{}", + "currentBytes:{} reservedBytes:{} peakBytes:{} cumulativeBytes:{} numAllocs:{} numFrees:{} numReserves:{} numReleases:{} numShrinks:{} numReclaims:{} numCollisions:{} numCapacityGrowths:{}", succinctBytes(currentBytes), succinctBytes(reservedBytes), succinctBytes(peakBytes), @@ -176,7 +176,8 @@ std::string MemoryPool::Stats::toString() const { numReleases, numShrinks, numReclaims, - numCollisions); + numCollisions, + numCapacityGrowths); } bool MemoryPool::Stats::operator==(const MemoryPool::Stats& other) const { @@ -189,7 +190,8 @@ bool MemoryPool::Stats::operator==(const MemoryPool::Stats& other) const { numFrees, numReserves, numReleases, - numCollisions) == + numCollisions, + numCapacityGrowths) == std::tie( other.currentBytes, other.reservedBytes, @@ -199,7 +201,8 @@ bool MemoryPool::Stats::operator==(const MemoryPool::Stats& other) const { other.numFrees, other.numReserves, other.numReleases, - other.numCollisions); + other.numCollisions, + other.numCapacityGrowths); } std::ostream& operator<<(std::ostream& os, const MemoryPool::Stats& stats) { @@ -449,6 +452,11 @@ MemoryPoolImpl::~MemoryPoolImpl() { "Bad memory usage track state: {}", toString()); + if (isRoot()) { + RECORD_HISTOGRAM_METRIC_VALUE( + kMetricMemoryPoolCapacityGrowCount, numCapacityGrowths_); + } + if (destructionCb_ != nullptr) { destructionCb_(this); } @@ -470,6 +478,7 @@ MemoryPool::Stats MemoryPoolImpl::statsLocked() const { stats.numReserves = numReserves_; stats.numReleases = numReleases_; stats.numCollisions = numCollisions_; + stats.numCapacityGrowths = numCapacityGrowths_; return stats; } @@ -795,6 +804,7 @@ bool MemoryPoolImpl::incrementReservationThreadSafe( VELOX_CHECK_NULL(parent_); + ++numCapacityGrowths_; if (growCapacityCb_(requestor, size)) { TestValue::adjust( "facebook::velox::memory::MemoryPoolImpl::incrementReservationThreadSafe::AfterGrowCallback", diff --git a/velox/common/memory/MemoryPool.h b/velox/common/memory/MemoryPool.h index 1dbe0d5470b3..bbbe06352747 100644 --- a/velox/common/memory/MemoryPool.h +++ b/velox/common/memory/MemoryPool.h @@ -448,6 +448,11 @@ class MemoryPool : public std::enable_shared_from_this { /// The number of internal memory reservation collisions caused by /// concurrent memory requests. uint64_t numCollisions{0}; + /// The number of memory capacity growth attempts through the memory + /// arbitration. + /// + /// NOTE: this only applies for the root memory pool. + uint64_t numCapacityGrowths{0}; bool operator==(const Stats& rhs) const; @@ -1017,20 +1022,26 @@ class MemoryPoolImpl : public MemoryPool { // Stats counters. // The number of memory allocations. - std::atomic numAllocs_{0}; + std::atomic_uint64_t numAllocs_{0}; // The number of memory frees. - std::atomic numFrees_{0}; + std::atomic_uint64_t numFrees_{0}; // The number of external memory reservations made through maybeReserve(). - std::atomic numReserves_{0}; + std::atomic_uint64_t numReserves_{0}; // The number of external memory releases made through release(). - std::atomic numReleases_{0}; + std::atomic_uint64_t numReleases_{0}; // The number of internal memory reservation collisions caused by concurrent // memory reservation requests. - std::atomic numCollisions_{0}; + std::atomic_uint64_t numCollisions_{0}; + + // The number of memory capacity growth attempts through the memory + // arbitration. + // + // NOTE: this only applies for root memory pool. + std::atomic_uint64_t numCapacityGrowths_{0}; // Mutex for 'debugAllocRecords_'. std::mutex debugAllocMutex_; diff --git a/velox/common/memory/tests/MemoryPoolTest.cpp b/velox/common/memory/tests/MemoryPoolTest.cpp index abed39691029..ec85726e24f4 100644 --- a/velox/common/memory/tests/MemoryPoolTest.cpp +++ b/velox/common/memory/tests/MemoryPoolTest.cpp @@ -2879,7 +2879,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { void* buf1 = leafChild1->allocate(bufferSize); ASSERT_EQ( leafChild1->stats().toString(), - "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( leafChild1->toString(), fmt::format( @@ -2888,7 +2888,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { isLeafThreadSafe_ ? "thread-safe" : "non-thread-safe")); ASSERT_EQ( leafChild2->stats().toString(), - "currentBytes:0B reservedBytes:0B peakBytes:0B cumulativeBytes:0B numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:0B reservedBytes:0B peakBytes:0B cumulativeBytes:0B numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( leafChild1->toString(), fmt::format( @@ -2897,36 +2897,36 @@ TEST_P(MemoryPoolTest, statsAndToString) { isLeafThreadSafe_ ? "thread-safe" : "non-thread-safe")); ASSERT_EQ( aggregateChild->stats().toString(), - "currentBytes:0B reservedBytes:0B peakBytes:0B cumulativeBytes:0B numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:0B reservedBytes:0B peakBytes:0B cumulativeBytes:0B numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( root->stats().toString(), - "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); void* buf2 = leafChild2->allocate(bufferSize); ASSERT_EQ( leafChild1->stats().toString(), - "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( leafChild2->stats().toString(), - "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( aggregateChild->stats().toString(), - "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( root->stats().toString(), - "currentBytes:2.00MB reservedBytes:2.00MB peakBytes:2.00MB cumulativeBytes:2.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:2.00MB reservedBytes:2.00MB peakBytes:2.00MB cumulativeBytes:2.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); leafChild1->free(buf1, bufferSize); ASSERT_EQ( leafChild1->stats().toString(), - "currentBytes:0B reservedBytes:0B peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:1 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:0B reservedBytes:0B peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:1 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( leafChild2->stats().toString(), - "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00KB reservedBytes:1.00MB peakBytes:1.00KB cumulativeBytes:1.00KB numAllocs:1 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( aggregateChild->stats().toString(), - "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:1.00MB cumulativeBytes:1.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); ASSERT_EQ( root->stats().toString(), - "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:2.00MB cumulativeBytes:2.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0"); + "currentBytes:1.00MB reservedBytes:1.00MB peakBytes:2.00MB cumulativeBytes:2.00MB numAllocs:0 numFrees:0 numReserves:0 numReleases:0 numShrinks:0 numReclaims:0 numCollisions:0 numCapacityGrowths:0"); leafChild2->free(buf2, bufferSize); std::vector bufs; for (int i = 0; i < 10; ++i) { @@ -2940,6 +2940,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(root->peakBytes(), 2097152); ASSERT_EQ(root->stats().cumulativeBytes, 3145728); ASSERT_EQ(root->stats().currentBytes, 1048576); + ASSERT_EQ(root->stats().numCapacityGrowths, 0); ASSERT_EQ(leafChild1->stats().numAllocs, 11); ASSERT_EQ(leafChild1->stats().numFrees, 1); ASSERT_EQ(leafChild1->stats().currentBytes, 10240); @@ -2947,6 +2948,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(leafChild1->stats().cumulativeBytes, 11264); ASSERT_EQ(leafChild1->stats().numReserves, 0); ASSERT_EQ(leafChild1->stats().numReleases, 0); + ASSERT_EQ(leafChild1->stats().numCapacityGrowths, 0); for (auto* buf : bufs) { leafChild1->free(buf, bufferSize); } @@ -2958,6 +2960,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(root->peakBytes(), 2097152); ASSERT_EQ(root->stats().cumulativeBytes, 3145728); ASSERT_EQ(root->stats().currentBytes, 0); + ASSERT_EQ(root->stats().numCapacityGrowths, 0); ASSERT_EQ(leafChild1->stats().numAllocs, 11); ASSERT_EQ(leafChild1->stats().numFrees, 11); ASSERT_EQ(leafChild1->stats().currentBytes, 0); @@ -2965,6 +2968,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(leafChild1->stats().cumulativeBytes, 11264); ASSERT_EQ(leafChild1->stats().numReserves, 0); ASSERT_EQ(leafChild1->stats().numReleases, 0); + ASSERT_EQ(leafChild1->stats().numCapacityGrowths, 0); leafChild1->maybeReserve(bufferSize); ASSERT_EQ(leafChild1->stats().numAllocs, 11); ASSERT_EQ(leafChild1->stats().numFrees, 11); @@ -2974,6 +2978,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(leafChild1->stats().cumulativeBytes, 11264); ASSERT_EQ(leafChild1->stats().numReserves, 1); ASSERT_EQ(leafChild1->stats().numReleases, 0); + ASSERT_EQ(leafChild1->stats().numCapacityGrowths, 0); leafChild1->release(); ASSERT_EQ(leafChild1->stats().numAllocs, 11); ASSERT_EQ(leafChild1->stats().numFrees, 11); @@ -2983,6 +2988,7 @@ TEST_P(MemoryPoolTest, statsAndToString) { ASSERT_EQ(leafChild1->peakBytes(), 10240); ASSERT_EQ(leafChild1->stats().numReserves, 1); ASSERT_EQ(leafChild1->stats().numReleases, 1); + ASSERT_EQ(leafChild1->stats().numCapacityGrowths, 0); } struct Buffer { diff --git a/velox/common/memory/tests/SharedArbitratorTest.cpp b/velox/common/memory/tests/SharedArbitratorTest.cpp index 50ffd6e3e7f7..05badf0306b1 100644 --- a/velox/common/memory/tests/SharedArbitratorTest.cpp +++ b/velox/common/memory/tests/SharedArbitratorTest.cpp @@ -402,6 +402,7 @@ DEBUG_ONLY_TEST_F(SharedArbitrationTest, reclaimToOrderBy) { ASSERT_GT(newStats.numReclaimedBytes, oldStats.numReclaimedBytes); ASSERT_GT(newStats.reclaimTimeUs, oldStats.reclaimTimeUs); ASSERT_EQ(arbitrator_->stats().numReserves, numAddedPools_); + ASSERT_GT(orderByQueryCtx->pool()->stats().numCapacityGrowths, 0); } } diff --git a/velox/docs/monitoring/metrics.rst b/velox/docs/monitoring/metrics.rst index b7ac875a92f2..c3bf283f8863 100644 --- a/velox/docs/monitoring/metrics.rst +++ b/velox/docs/monitoring/metrics.rst @@ -156,6 +156,16 @@ Memory Management - Average - The average of total free memory capacity which is managed by the memory arbitrator. + * - memory_pool_initial_capacity_bytes + - Histogram + - The distribution of a root memory pool's initial capacity in range of [0 256MB] + with 32 buckets. It is configured to report the capacity at P50, P90, P99, + and P100 percentiles. + * - memory_pool_capacity_growth_count + - Histogram + - The distribution of a root memory pool cappacity growth attemps through + memory arbitration in range of [0, 256] with 32 buckets. It is configured + to report the count at P50, P90, P99, and P100 percentiles. * - memory_pool_usage_leak_bytes - Sum - The leaf memory pool usage leak in bytes.