From e99120c25726b42458cd3586712cba80f468a488 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Mon, 22 Jun 2015 21:02:41 +0300 Subject: [PATCH] Memory pool usage tracking + size optimization --- libuavcan/include/uavcan/dynamic_memory.hpp | 76 ++++++++++++------- libuavcan/src/uc_dynamic_memory.cpp | 2 +- libuavcan/test/dynamic_memory.cpp | 9 +++ .../test/transport/transfer_test_helpers.hpp | 2 +- 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/libuavcan/include/uavcan/dynamic_memory.hpp b/libuavcan/include/uavcan/dynamic_memory.hpp index 18c953f3c7..5d440724d6 100644 --- a/libuavcan/include/uavcan/dynamic_memory.hpp +++ b/libuavcan/include/uavcan/dynamic_memory.hpp @@ -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 +template 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(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(min(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 -PoolAllocator::PoolAllocator() - : free_list_(reinterpret_cast(pool_.bytes)) +template +const uint16_t PoolAllocator::NumBlocks; + +template +PoolAllocator::PoolAllocator() : + free_list_(reinterpret_cast(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::PoolAllocator() free_list_[NumBlocks - 1].next = NULL; } -template +template void* PoolAllocator::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 +template void PoolAllocator::deallocate(const void* ptr) { if (ptr == NULL) { return; } + Node* p = static_cast(const_cast(ptr)); p->next = free_list_; free_list_ = p; -} -template -unsigned PoolAllocator::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_--; } } diff --git a/libuavcan/src/uc_dynamic_memory.cpp b/libuavcan/src/uc_dynamic_memory.cpp index 459e04023d..68925f1d11 100644 --- a/libuavcan/src/uc_dynamic_memory.cpp +++ b/libuavcan/src/uc_dynamic_memory.cpp @@ -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()); } diff --git a/libuavcan/test/dynamic_memory.cpp b/libuavcan/test/dynamic_memory.cpp index f87c537df3..21ba7e4487 100644 --- a/libuavcan/test/dynamic_memory.cpp +++ b/libuavcan/test/dynamic_memory.cpp @@ -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()); } diff --git a/libuavcan/test/transport/transfer_test_helpers.hpp b/libuavcan/test/transport/transfer_test_helpers.hpp index 14fb9e58c8..af06f998e8 100644 --- a/libuavcan/test/transport/transfer_test_helpers.hpp +++ b/libuavcan/test/transport/transfer_test_helpers.hpp @@ -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; } };