- 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)
|
/// transparent passthrough to the global allocator (i.e. allocate_shared behaves like make_shared)
|
||||||
/// for A/B benchmarking without touching the call sites.
|
/// for A/B benchmarking without touching the call sites.
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -45,6 +46,7 @@ namespace hl
|
|||||||
{
|
{
|
||||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||||
tagOf(raw) = TagGlobal;
|
tagOf(raw) = TagGlobal;
|
||||||
|
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
|
||||||
return raw + HeaderSize;
|
return raw + HeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ namespace hl
|
|||||||
|
|
||||||
uint8_t* block = static_cast<uint8_t*>(head);
|
uint8_t* block = static_cast<uint8_t*>(head);
|
||||||
head = nextOf(block);
|
head = nextOf(block);
|
||||||
|
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
|
||||||
return block + HeaderSize;
|
return block + HeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,14 +71,49 @@ namespace hl
|
|||||||
void*& head = freeListHead();
|
void*& head = freeListHead();
|
||||||
nextOf(block) = head;
|
nextOf(block) = head;
|
||||||
head = block;
|
head = block;
|
||||||
|
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
::operator delete(static_cast<void*>(block));
|
::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:
|
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 =
|
static constexpr std::size_t HeaderSize =
|
||||||
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
||||||
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
||||||
@@ -114,6 +152,7 @@ namespace hl
|
|||||||
{
|
{
|
||||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
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);
|
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||||
@@ -144,6 +183,21 @@ namespace hl
|
|||||||
};
|
};
|
||||||
#endif // HL_RTP_POOL
|
#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
|
/// @class PoolAllocator
|
||||||
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
/// 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
|
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
||||||
|
|||||||
Reference in New Issue
Block a user