- statistics for pool allocator
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
/// transparent passthrough to the global allocator (i.e. allocate_shared behaves like make_shared)
|
||||
/// for A/B benchmarking without touching the call sites.
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
@@ -45,6 +46,7 @@ namespace hl
|
||||
{
|
||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||
tagOf(raw) = TagGlobal;
|
||||
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
|
||||
return raw + HeaderSize;
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ namespace hl
|
||||
|
||||
uint8_t* block = static_cast<uint8_t*>(head);
|
||||
head = nextOf(block);
|
||||
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
|
||||
return block + HeaderSize;
|
||||
}
|
||||
|
||||
@@ -68,14 +71,49 @@ namespace hl
|
||||
void*& head = freeListHead();
|
||||
nextOf(block) = head;
|
||||
head = block;
|
||||
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(static_cast<void*>(block));
|
||||
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// @name Diagnostics (relaxed gauges — not synchronization)
|
||||
/// @{
|
||||
/// Blocks currently handed out from the pool = pooled allocate() minus
|
||||
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
|
||||
/// which only ever grows) this DROPS when objects are freed, so a rising
|
||||
/// activeBlocks() is a genuine leak of referenced objects rather than a
|
||||
/// traffic peak that merely carved extra chunks. Covers every pooled
|
||||
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
|
||||
static std::int64_t activeBlocks() noexcept
|
||||
{
|
||||
return s_activeBlocks.load(std::memory_order_relaxed);
|
||||
}
|
||||
/// Live oversized allocations that overflowed to ::operator new.
|
||||
static std::int64_t activeGlobalAllocations() noexcept
|
||||
{
|
||||
return s_activeGlobal.load(std::memory_order_relaxed);
|
||||
}
|
||||
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
|
||||
static std::int64_t capacityBlocks() noexcept
|
||||
{
|
||||
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
|
||||
}
|
||||
/// Approximate live pooled payload bytes (excludes per-block header).
|
||||
static std::int64_t activeBytes() noexcept
|
||||
{
|
||||
return activeBlocks() * static_cast<std::int64_t>(PayloadSize);
|
||||
}
|
||||
/// @}
|
||||
|
||||
private:
|
||||
inline static std::atomic<std::int64_t> s_activeBlocks{0};
|
||||
inline static std::atomic<std::int64_t> s_activeGlobal{0};
|
||||
inline static std::atomic<std::int64_t> s_chunks{0};
|
||||
|
||||
static constexpr std::size_t HeaderSize =
|
||||
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
||||
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
||||
@@ -114,6 +152,7 @@ namespace hl
|
||||
{
|
||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
||||
s_chunks.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
@@ -144,6 +183,21 @@ namespace hl
|
||||
};
|
||||
#endif // HL_RTP_POOL
|
||||
|
||||
/// Mode-independent accessors for the pool diagnostics, so callers need not know
|
||||
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
|
||||
/// ("not applicable") since allocate_shared then behaves like make_shared.
|
||||
#if HL_RTP_POOL
|
||||
inline std::int64_t poolActiveBlocks() noexcept { return FixedBlockPool::activeBlocks(); }
|
||||
inline std::int64_t poolCapacityBlocks() noexcept { return FixedBlockPool::capacityBlocks(); }
|
||||
inline std::int64_t poolActiveGlobal() noexcept { return FixedBlockPool::activeGlobalAllocations(); }
|
||||
inline std::int64_t poolActiveBytes() noexcept { return FixedBlockPool::activeBytes(); }
|
||||
#else
|
||||
inline std::int64_t poolActiveBlocks() noexcept { return -1; }
|
||||
inline std::int64_t poolCapacityBlocks() noexcept { return -1; }
|
||||
inline std::int64_t poolActiveGlobal() noexcept { return -1; }
|
||||
inline std::int64_t poolActiveBytes() noexcept { return -1; }
|
||||
#endif
|
||||
|
||||
/// @class PoolAllocator
|
||||
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
||||
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
||||
|
||||
Reference in New Issue
Block a user