Skip to content

Commit

Permalink
Add metrics to track initial memory capacity and growth (facebookincu…
Browse files Browse the repository at this point in the history
…bator#9416)

Summary:
Add velox.memory_pool_initial_capacity_bytes as histogram to track the query memory pool's
initial capacity distribution. This indicates if the overall memory arbitration is under high query
memory usage pressure. If a small query starts with sufficient amount of memory, then it can
run through completion without the interference of the slow memory arbitration by some large
query.

Add elox.memory_pool_capacity_growth_count to track the number of query memory pool
capacity growth attempts through memory arbitrations. The less number of growth, the less
interference with the memory arbitration by the other queries.

Both metrics can be used to evaluate the effectiveness of the followup optimizations: reserved
query capacity and memory capacity growth control within memory arbitrator

Pull Request resolved: facebookincubator#9416

Reviewed By: bikramSingh91, oerling

Differential Revision: D55925034

Pulled By: xiaoxmeng

fbshipit-source-id: fe289e43f1eb99e4b798d32cdb82103dd4d8d04b
  • Loading branch information
xiaoxmeng authored and facebook-github-bot committed Apr 9, 2024
1 parent 845f13c commit 54a7fe8
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 21 deletions.
20 changes: 20 additions & 0 deletions velox/common/base/Counters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions velox/common/base/Counters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"};

Expand Down
5 changes: 5 additions & 0 deletions velox/common/memory/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -207,6 +210,8 @@ std::shared_ptr<MemoryPool> MemoryManager::addRootPool(
VELOX_CHECK_EQ(pool->capacity(), 0);
arbitrator_->growCapacity(
pool.get(), std::min<uint64_t>(poolInitCapacity_, maxCapacity));
RECORD_HISTOGRAM_METRIC_VALUE(
kMetricMemoryPoolInitialCapacityBytes, pool->capacity());
return pool;
}

Expand Down
18 changes: 14 additions & 4 deletions velox/common/memory/MemoryPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand All @@ -470,6 +478,7 @@ MemoryPool::Stats MemoryPoolImpl::statsLocked() const {
stats.numReserves = numReserves_;
stats.numReleases = numReleases_;
stats.numCollisions = numCollisions_;
stats.numCapacityGrowths = numCapacityGrowths_;
return stats;
}

Expand Down Expand Up @@ -795,6 +804,7 @@ bool MemoryPoolImpl::incrementReservationThreadSafe(

VELOX_CHECK_NULL(parent_);

++numCapacityGrowths_;
if (growCapacityCb_(requestor, size)) {
TestValue::adjust(
"facebook::velox::memory::MemoryPoolImpl::incrementReservationThreadSafe::AfterGrowCallback",
Expand Down
21 changes: 16 additions & 5 deletions velox/common/memory/MemoryPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,11 @@ class MemoryPool : public std::enable_shared_from_this<MemoryPool> {
/// 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;

Expand Down Expand Up @@ -1017,20 +1022,26 @@ class MemoryPoolImpl : public MemoryPool {

// Stats counters.
// The number of memory allocations.
std::atomic<uint64_t> numAllocs_{0};
std::atomic_uint64_t numAllocs_{0};

// The number of memory frees.
std::atomic<uint64_t> numFrees_{0};
std::atomic_uint64_t numFrees_{0};

// The number of external memory reservations made through maybeReserve().
std::atomic<uint64_t> numReserves_{0};
std::atomic_uint64_t numReserves_{0};

// The number of external memory releases made through release().
std::atomic<uint64_t> numReleases_{0};
std::atomic_uint64_t numReleases_{0};

// The number of internal memory reservation collisions caused by concurrent
// memory reservation requests.
std::atomic<uint64_t> 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_;
Expand Down
30 changes: 18 additions & 12 deletions velox/common/memory/tests/MemoryPoolTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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<void*> bufs;
for (int i = 0; i < 10; ++i) {
Expand All @@ -2940,13 +2940,15 @@ 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);
ASSERT_EQ(leafChild1->stats().peakBytes, 10240);
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);
}
Expand All @@ -2958,13 +2960,15 @@ 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);
ASSERT_EQ(leafChild1->stats().peakBytes, 10240);
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);
Expand All @@ -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);
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions velox/common/memory/tests/SharedArbitratorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
10 changes: 10 additions & 0 deletions velox/docs/monitoring/metrics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 54a7fe8

Please sign in to comment.