From eeda3aaa56cf8adf8d43cd79a697b36422ee42b9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 May 2024 22:46:14 +0200 Subject: [PATCH] Library: remove elaborate allocation logic ...due to the decision to use a much simpler allocation scheme to increase probability for actual savings, after switching the API and removing all trading related aspects, a lot of further code is obsoleted --- src/lib/allocation-cluster.cpp | 226 +------------------------ src/lib/allocation-cluster.hpp | 163 ------------------ tests/library/linked-elements-test.cpp | 10 +- wiki/thinkPad.ichthyo.mm | 4 +- 4 files changed, 9 insertions(+), 394 deletions(-) diff --git a/src/lib/allocation-cluster.cpp b/src/lib/allocation-cluster.cpp index d9389c270..cbf6ea039 100644 --- a/src/lib/allocation-cluster.cpp +++ b/src/lib/allocation-cluster.cpp @@ -32,154 +32,13 @@ #include "lib/allocation-cluster.hpp" #include "lib/error.hpp" #include "lib/util.hpp" -#include "lib/sync.hpp" -#include using util::isnil; namespace lib { - /** - * "Low-level" Memory manager for allocating small objects of a fixed size. - * The usage pattern is definite: Objects will be allocated in the course of - * a build process and then live until all Objects will be purged in one sway. - * Allocations will be requested one by one and immediately committed after - * successful ctor call of the object being allocated. Allocations and commits - * can be assumed to come in pairs, thus if an allocation immediately follows - * another one (without commit), the previous allocation can be considered - * a failure and can be dropped silently. After an allocation succeeds - * (i.e. was committed), the MemoryManager is in charge for the lifecycle - * of the object within the allocated space and has to guarantee calling - * it's dtor, either on shutdown or on explicit #purge() -- the type info - * structure handed in on initialisation provides a means for invoking - * the dtor without actually knowing the object's type. - * - * @todo this is a preliminary or pseudo-implementation based on - * a vector of raw pointers, i.e. actually the objects are heap - * allocated. What actually should happen is for the MemoryManager to - * allocate raw memory chunk wise, sub partition it and place the objects - * into this private memory buffer. Further, possibly we could maintain - * a pool of raw memory chunks used by all MemoryManager instances. I am - * skipping those details for now (10/2008) because they should be based - * on real-world measurements, not guessing. - */ - class AllocationCluster::MemoryManager - : public Sync - { - typedef std::deque MemTable; - TypeInfo type_; - MemTable mem_; - size_t top_; ///< index of the next slot available for allocation - - public: - MemoryManager(TypeInfo info) : top_(0) { reset(info); } - ~MemoryManager() { purge(); } - - size_t size() const; - - void purge(); - void reset(TypeInfo info); - - void* allocate(); - - void commit (void* pendingAlloc); - - private: - void clearStorage(); - }; - - - - /** the top_ index always points at the next slot - * not yet holding a finished, committed allocation. - * Index is zero based, thus top_ == count of living objects - */ - size_t - AllocationCluster::MemoryManager::size() const - { - return top_; - } - - - void - AllocationCluster::MemoryManager::reset (TypeInfo info) - { - Lock sync{this}; - - if (0 < mem_.size()) purge(); - type_ = info; - - ENSURE (0==top_); - ENSURE (isnil (mem_)); - ENSURE (0 < type_.allocSize); - ENSURE (type_.killIt); - } - - - void - AllocationCluster::MemoryManager::purge() - { - Lock sync{this}; - - REQUIRE (type_.killIt, "we need a deleter function"); - REQUIRE (0 < type_.allocSize, "allocation size unknown"); - REQUIRE (top_ == mem_.size() || (top_+1) == mem_.size()); - - while (top_) - type_.killIt (mem_[--top_]); - clearStorage(); - }// note: unnecessary to kill pending allocations - - - inline void - AllocationCluster::MemoryManager::clearStorage() - { - for (size_t i=mem_.size(); 0 < i; ) - delete[] mem_[--i]; - mem_.clear(); - } - - - inline void* - AllocationCluster::MemoryManager::allocate() - { - Lock sync{this}; - - REQUIRE (0 < type_.allocSize); - REQUIRE (top_ <= mem_.size()); - - if (top_==mem_.size()) - mem_.resize(top_+1); - - if (!mem_[top_]) // re-use existing allocation, if any - mem_[top_] = new char[type_.allocSize]; - - ENSURE (top_ < mem_.size()); - ENSURE (mem_[top_]); - return mem_[top_]; - } - - - inline void - AllocationCluster::MemoryManager::commit (void* pendingAlloc) - { - Lock sync{this}; - - REQUIRE (pendingAlloc); - ASSERT (top_ < mem_.size()); - ASSERT (pendingAlloc == mem_[top_], "allocation protocol violated"); - - ++top_; - - ENSURE (top_ == mem_.size()); - } - - - - /** storage for static bookkeeping of type allocation slots */ - size_t AllocationCluster::maxTypeIDs; /** creating a new AllocationCluster prepares a table capable @@ -199,99 +58,18 @@ namespace lib { AllocationCluster::~AllocationCluster() noexcept { try - { // avoiding a per-instance lock for now. - ClassLock guard; // (see note in the class description) + { TRACE (memory, "shutting down AllocationCluster"); - for (size_t i = typeHandlers_.size(); 0 < i; --i) - if (handler(i)) - handler(i)->purge(); - - typeHandlers_.clear(); } - catch (lumiera::Error & ex) - { - WARN (progress, "Exception while closing AllocationCluster: %s", ex.what()); - const char* errID = lumiera_error(); - TRACE (debugging, "Error flag was: %s", errID); - } - catch (...) - { - ALERT (progress, "Unexpected fatal Exception while closing AllocationCluster."); - lumiera::error::lumiera_unexpectedException(); // terminate - } + ERROR_LOG_AND_IGNORE (progress, "discarding AllocationCluster") } - void* - AllocationCluster::initiateAlloc (size_t& slot) - { - if (!slot or slot > typeHandlers_.size() or !handler(slot) ) - return nullptr; // Memory manager not yet initialised - else - return handler(slot)->allocate(); - } - - - void* - AllocationCluster::initiateAlloc (TypeInfo type, size_t& slot) - { - ASSERT (0 < slot); - - { // avoiding a per-instance lock for now. - ClassLock guard; // (see note in the class description) - - if (slot > typeHandlers_.size()) - typeHandlers_.resize(slot); - if (not handler(slot)) - handler(slot).reset (new MemoryManager (type)); - - } - - ASSERT (handler(slot)); - return initiateAlloc(slot); - } - - - void - AllocationCluster::finishAlloc (size_t& slot, void* allocatedObj) - { - ASSERT (handler(slot)); - ASSERT (allocatedObj); - - handler(slot)->commit(allocatedObj); - } - - /* === diagnostics helpers === */ - /** @return total number of objects - * currently managed by this allocator */ - size_t - AllocationCluster::size() const - { - size_t size(0); - typedef ManagerTable::const_iterator Iter; - - for (Iter ii= typeHandlers_.begin(); ii != typeHandlers_.end(); ++ii ) - if (*ii) - size += (*ii)->size(); - - return size; - } - - - size_t - AllocationCluster::countActiveInstances (size_t& slot) const - { - if (handler (slot)) - return handler(slot)->size(); - else - return 0; - } - } // namespace lib diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index a443e7485..360d779a5 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -122,11 +122,6 @@ namespace lib { /* === diagnostics === */ - size_t size() const; - - template - size_t count() const; - size_t numExtents() const { @@ -157,73 +152,6 @@ namespace lib { { return static_cast (allotMemory (cnt * sizeof(X))); } - - - /** initiate an allocation for the given type */ - template - void* - allocation(); - - /** finish the allocation after the ctor is successful */ - template - TY& - commit (TY* obj); - - - /** - * The type-specific configuration information - * any low-level memory manager needs to know - */ - struct TypeInfo; - - /** - * low-level memory manager responsible for - * the allocations of one specific type. - */ - class MemoryManager; - - /** - * organising the association Type -> table entry - */ - template - struct TypeSlot; - - static size_t maxTypeIDs; - - - using HMemManager = ScopedPtrHolder; - using Allo = Allocator_TransferNoncopyable; - using ManagerTable = std::vector; - - ManagerTable typeHandlers_; ///< table of active MemoryManager instances - - - - HMemManager& - handler (size_t slot) - { - ASSERT (0 - TypeInfo(TY*) - : allocSize(sizeof(TY)), - killIt(&TypeSlot::kill) - { } - - TypeInfo() ///< denotes "unknown" type - : allocSize(0), - killIt(0) - { } - }; - - - - template - struct AllocationCluster::TypeSlot - { - static size_t id_; ///< table pos+1 of the memory manager in charge for type TY - - static size_t & - get() - { - return id_; - } - - static TypeInfo - setup() - { - ClassLock guard; - if (0 == id_) - id_= ++maxTypeIDs; - - return TypeInfo ((TY*) 0 ); - } - - static void - kill (void* obj) - { - TY* p = static_cast(obj); - ASSERT (p); - ASSERT (INSTANCEOF (TY,p)); - p->~TY(); - } - - }; - - - /** storage for static bookkeeping of type allocation slots */ - template - size_t AllocationCluster::TypeSlot::id_; - - - - template - inline void* - AllocationCluster::allocation() - { - void *mem = initiateAlloc (TypeSlot::get()); - if (not mem) - mem = initiateAlloc (TypeSlot::setup(),TypeSlot::get()); - ENSURE (mem); - return mem; - } - - template - inline TY& - AllocationCluster::commit (TY* obj) - { - REQUIRE (obj); - finishAlloc (TypeSlot::get(), obj); - return *obj; - } - - - /** helper for diagnostics - * @return number of currently allocated - * object instances of the given type - */ - template - size_t - AllocationCluster::count() const - { - return countActiveInstances (TypeSlot::get()); - } - - } // namespace lib #endif /*LIB_ALLOCATION_CLUSTER_H*/ diff --git a/tests/library/linked-elements-test.cpp b/tests/library/linked-elements-test.cpp index ef65d8520..bc6f6fd93 100644 --- a/tests/library/linked-elements-test.cpp +++ b/tests/library/linked-elements-test.cpp @@ -457,7 +457,7 @@ namespace test{ * all the elements might be added in one sway, by pulling them * from a Lumiera Forward Iterator. In case this is done in the * ctor, any exception while doing so will trigger cleanup - * of all elements (and then failure of the ctor alltogether) + * of all elements (and then failure of the ctor altogether) */ void verify_RAII_safety() @@ -486,11 +486,9 @@ namespace test{ elements.emplace> (4,5); elements.emplace> (7,8,9); + const size_t EXPECT = sizeof(Num<1>) + sizeof(Num<3>) + sizeof(Num<6>); + CHECK (EXPECT == allocator.numBytes()); CHECK (sum(9) == Dummy::checksum()); - CHECK (3 == allocator.size()); - CHECK (1 == allocator.count>()); - CHECK (1 == allocator.count>()); - CHECK (1 == allocator.count>()); CHECK (3 == elements.size()); CHECK (1+2 == elements[2].getVal()); @@ -498,7 +496,7 @@ namespace test{ CHECK (6+7+8+9 == elements[0].getVal()); elements.clear(); - CHECK (3 == allocator.size()); + CHECK (EXPECT == allocator.numBytes()); CHECK (sum(9) == Dummy::checksum()); // note: elements won't be discarded unless // the AllocationCluster goes out of scope diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 35e1f8e56..364db1b8f 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -81689,6 +81689,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + @@ -81941,8 +81943,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +