mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-12 17:03:35 +08:00
Memory pool usage tracking + size optimization
This commit is contained in:
@@ -26,13 +26,13 @@ public:
|
||||
virtual void* allocate(std::size_t size) = 0;
|
||||
virtual void deallocate(const void* ptr) = 0;
|
||||
|
||||
virtual std::size_t getNumBlocks() const = 0;
|
||||
virtual uint16_t getNumBlocks() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Classic implementation of a pool allocator (Meyers).
|
||||
*/
|
||||
template <std::size_t PoolSize, std::size_t BlockSize>
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator, Noncopyable
|
||||
{
|
||||
union Node
|
||||
@@ -50,18 +50,29 @@ class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator, Noncopyable
|
||||
Node _aligner3;
|
||||
} pool_;
|
||||
|
||||
uint16_t used_;
|
||||
uint16_t max_used_;
|
||||
|
||||
public:
|
||||
static const unsigned NumBlocks = unsigned(PoolSize / BlockSize);
|
||||
static const uint16_t NumBlocks = PoolSize / BlockSize;
|
||||
|
||||
PoolAllocator();
|
||||
|
||||
virtual void* allocate(std::size_t size);
|
||||
virtual void deallocate(const void* ptr);
|
||||
|
||||
virtual std::size_t getNumBlocks() const { return NumBlocks; }
|
||||
virtual uint16_t getNumBlocks() const { return NumBlocks; }
|
||||
|
||||
unsigned getNumFreeBlocks() const;
|
||||
unsigned getNumUsedBlocks() const { return NumBlocks - getNumFreeBlocks(); }
|
||||
/**
|
||||
* Return the number of blocks that are currently allocated/unallocated.
|
||||
*/
|
||||
uint16_t getNumUsedBlocks() const { return used_; }
|
||||
uint16_t getNumFreeBlocks() const { return static_cast<uint16_t>(NumBlocks - used_); }
|
||||
|
||||
/**
|
||||
* Returns the maximum number of blocks that were ever allocated at the same time.
|
||||
*/
|
||||
uint16_t getPeakNumUsedBlocks() const { return max_used_; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -70,13 +81,13 @@ public:
|
||||
class LimitedPoolAllocator : public IPoolAllocator
|
||||
{
|
||||
IPoolAllocator& allocator_;
|
||||
const std::size_t max_blocks_;
|
||||
std::size_t used_blocks_;
|
||||
const uint16_t max_blocks_;
|
||||
uint16_t used_blocks_;
|
||||
|
||||
public:
|
||||
LimitedPoolAllocator(IPoolAllocator& allocator, std::size_t max_blocks)
|
||||
: allocator_(allocator)
|
||||
, max_blocks_(max_blocks)
|
||||
, max_blocks_(static_cast<uint16_t>(min<std::size_t>(max_blocks, 0xFFFFU)))
|
||||
, used_blocks_(0)
|
||||
{
|
||||
UAVCAN_ASSERT(max_blocks_ > 0);
|
||||
@@ -85,7 +96,7 @@ public:
|
||||
virtual void* allocate(std::size_t size);
|
||||
virtual void deallocate(const void* ptr);
|
||||
|
||||
virtual std::size_t getNumBlocks() const;
|
||||
virtual uint16_t getNumBlocks() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -93,10 +104,18 @@ public:
|
||||
/*
|
||||
* PoolAllocator<>
|
||||
*/
|
||||
template <std::size_t PoolSize, std::size_t BlockSize>
|
||||
PoolAllocator<PoolSize, BlockSize>::PoolAllocator()
|
||||
: free_list_(reinterpret_cast<Node*>(pool_.bytes))
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
const uint16_t PoolAllocator<PoolSize, BlockSize>::NumBlocks;
|
||||
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
PoolAllocator<PoolSize, BlockSize>::PoolAllocator() :
|
||||
free_list_(reinterpret_cast<Node*>(pool_.bytes)),
|
||||
used_(0),
|
||||
max_used_(0)
|
||||
{
|
||||
// The limit is imposed by the width of the pool usage tracking variables.
|
||||
StaticAssert<((PoolSize / BlockSize) <= 0xFFFFU)>::check();
|
||||
|
||||
(void)std::memset(pool_.bytes, 0, PoolSize);
|
||||
for (unsigned i = 0; (i + 1) < (NumBlocks - 1 + 1); i++) // -Werror=type-limits
|
||||
{
|
||||
@@ -106,42 +125,43 @@ PoolAllocator<PoolSize, BlockSize>::PoolAllocator()
|
||||
free_list_[NumBlocks - 1].next = NULL;
|
||||
}
|
||||
|
||||
template <std::size_t PoolSize, std::size_t BlockSize>
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
void* PoolAllocator<PoolSize, BlockSize>::allocate(std::size_t size)
|
||||
{
|
||||
if (free_list_ == NULL || size > BlockSize)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* pmem = free_list_;
|
||||
free_list_ = free_list_->next;
|
||||
|
||||
// Statistics
|
||||
UAVCAN_ASSERT(used_ < NumBlocks);
|
||||
used_++;
|
||||
if (used_ > max_used_)
|
||||
{
|
||||
max_used_ = used_;
|
||||
}
|
||||
|
||||
return pmem;
|
||||
}
|
||||
|
||||
template <std::size_t PoolSize, std::size_t BlockSize>
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
void PoolAllocator<PoolSize, BlockSize>::deallocate(const void* ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Node* p = static_cast<Node*>(const_cast<void*>(ptr));
|
||||
p->next = free_list_;
|
||||
free_list_ = p;
|
||||
}
|
||||
|
||||
template <std::size_t PoolSize, std::size_t BlockSize>
|
||||
unsigned PoolAllocator<PoolSize, BlockSize>::getNumFreeBlocks() const
|
||||
{
|
||||
unsigned num = 0;
|
||||
Node* p = free_list_;
|
||||
while (p)
|
||||
{
|
||||
num++;
|
||||
UAVCAN_ASSERT(num <= NumBlocks);
|
||||
p = p->next;
|
||||
}
|
||||
return num;
|
||||
// Statistics
|
||||
UAVCAN_ASSERT(used_ > 0);
|
||||
used_--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ void LimitedPoolAllocator::deallocate(const void* ptr)
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t LimitedPoolAllocator::getNumBlocks() const
|
||||
uint16_t LimitedPoolAllocator::getNumBlocks() const
|
||||
{
|
||||
return min(max_blocks_, allocator_.getNumBlocks());
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ TEST(DynamicMemory, Basic)
|
||||
{
|
||||
uavcan::PoolAllocator<128, 32> pool32;
|
||||
EXPECT_EQ(4, pool32.getNumFreeBlocks());
|
||||
EXPECT_EQ(0, pool32.getPeakNumUsedBlocks());
|
||||
const void* ptr1 = pool32.allocate(16);
|
||||
ASSERT_TRUE(ptr1);
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks());
|
||||
@@ -16,6 +17,7 @@ TEST(DynamicMemory, Basic)
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks());
|
||||
pool32.deallocate(ptr1);
|
||||
EXPECT_EQ(0, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(1, pool32.getPeakNumUsedBlocks());
|
||||
}
|
||||
|
||||
TEST(DynamicMemory, OutOfMemory)
|
||||
@@ -24,18 +26,22 @@ TEST(DynamicMemory, OutOfMemory)
|
||||
|
||||
EXPECT_EQ(2, pool32.getNumFreeBlocks());
|
||||
EXPECT_EQ(0, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(0, pool32.getPeakNumUsedBlocks());
|
||||
|
||||
const void* ptr1 = pool32.allocate(32);
|
||||
ASSERT_TRUE(ptr1);
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(1, pool32.getPeakNumUsedBlocks());
|
||||
|
||||
const void* ptr2 = pool32.allocate(32);
|
||||
ASSERT_TRUE(ptr2);
|
||||
EXPECT_EQ(2, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(2, pool32.getPeakNumUsedBlocks());
|
||||
|
||||
ASSERT_FALSE(pool32.allocate(32)); // No free blocks left --> NULL
|
||||
EXPECT_EQ(2, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(0, pool32.getNumFreeBlocks());
|
||||
EXPECT_EQ(2, pool32.getPeakNumUsedBlocks());
|
||||
}
|
||||
|
||||
TEST(DynamicMemory, LimitedPoolAllocator)
|
||||
@@ -44,6 +50,7 @@ TEST(DynamicMemory, LimitedPoolAllocator)
|
||||
uavcan::LimitedPoolAllocator lim(pool32, 2);
|
||||
|
||||
EXPECT_EQ(2, lim.getNumBlocks());
|
||||
EXPECT_EQ(0, pool32.getPeakNumUsedBlocks());
|
||||
|
||||
const void* ptr1 = lim.allocate(1);
|
||||
const void* ptr2 = lim.allocate(1);
|
||||
@@ -62,4 +69,6 @@ TEST(DynamicMemory, LimitedPoolAllocator)
|
||||
EXPECT_TRUE(ptr4);
|
||||
EXPECT_TRUE(ptr5);
|
||||
EXPECT_FALSE(ptr6);
|
||||
|
||||
EXPECT_EQ(2, pool32.getPeakNumUsedBlocks());
|
||||
}
|
||||
|
||||
@@ -332,5 +332,5 @@ class NullAllocator : public uavcan::IPoolAllocator
|
||||
public:
|
||||
virtual void* allocate(std::size_t) { return NULL; }
|
||||
virtual void deallocate(const void*) { }
|
||||
virtual std::size_t getNumBlocks() const { return 0; }
|
||||
virtual uint16_t getNumBlocks() const { return 0; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user