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
This commit is contained in:
Fischlurch 2024-05-15 22:46:14 +02:00
parent 13f51c910c
commit eeda3aaa56
4 changed files with 9 additions and 394 deletions

View file

@ -32,154 +32,13 @@
#include "lib/allocation-cluster.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"
#include "lib/sync.hpp"
#include <deque>
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<RecursiveLock_NoWait>
{
typedef std::deque<char*> 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<AllocationCluster> 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<AllocationCluster> 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

View file

@ -122,11 +122,6 @@ namespace lib {
/* === diagnostics === */
size_t size() const;
template<class TY>
size_t count() const;
size_t
numExtents() const
{
@ -157,73 +152,6 @@ namespace lib {
{
return static_cast<X*> (allotMemory (cnt * sizeof(X)));
}
/** initiate an allocation for the given type */
template<class TY>
void*
allocation();
/** finish the allocation after the ctor is successful */
template<class TY>
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<class TY>
struct TypeSlot;
static size_t maxTypeIDs;
using HMemManager = ScopedPtrHolder<MemoryManager>;
using Allo = Allocator_TransferNoncopyable<HMemManager>;
using ManagerTable = std::vector<HMemManager,Allo>;
ManagerTable typeHandlers_; ///< table of active MemoryManager instances
HMemManager&
handler (size_t slot)
{
ASSERT (0<slot && slot<=typeHandlers_.size());
return typeHandlers_[slot-1];
}
HMemManager const&
handler (size_t slot) const
{
ASSERT (0<slot && slot<=typeHandlers_.size());
return typeHandlers_[slot-1];
}
/** implementation of the actual memory allocation
* is pushed down to the MemoryManager impl. */
void* initiateAlloc (size_t& slot);
void* initiateAlloc (TypeInfo type, size_t& slot);
/** enrol the allocation after successful ctor call */
void finishAlloc (size_t& slot, void*);
/** @internal helper for diagnostics,
* delegating to actual memory manager */
size_t countActiveInstances (size_t& slot) const;
};
@ -232,97 +160,6 @@ namespace lib {
//-----implementation-details------------------------
struct AllocationCluster::TypeInfo
{
size_t allocSize;
void (*killIt)(void*); ///< deleter function
template<class TY>
TypeInfo(TY*)
: allocSize(sizeof(TY)),
killIt(&TypeSlot<TY>::kill)
{ }
TypeInfo() ///< denotes "unknown" type
: allocSize(0),
killIt(0)
{ }
};
template<class TY>
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<AllocationCluster> guard;
if (0 == id_)
id_= ++maxTypeIDs;
return TypeInfo ((TY*) 0 );
}
static void
kill (void* obj)
{
TY* p = static_cast<TY*>(obj);
ASSERT (p);
ASSERT (INSTANCEOF (TY,p));
p->~TY();
}
};
/** storage for static bookkeeping of type allocation slots */
template<class TY>
size_t AllocationCluster::TypeSlot<TY>::id_;
template<class TY>
inline void*
AllocationCluster::allocation()
{
void *mem = initiateAlloc (TypeSlot<TY>::get());
if (not mem)
mem = initiateAlloc (TypeSlot<TY>::setup(),TypeSlot<TY>::get());
ENSURE (mem);
return mem;
}
template<class TY>
inline TY&
AllocationCluster::commit (TY* obj)
{
REQUIRE (obj);
finishAlloc (TypeSlot<TY>::get(), obj);
return *obj;
}
/** helper for diagnostics
* @return number of currently allocated
* object instances of the given type
*/
template<class TY>
size_t
AllocationCluster::count() const
{
return countActiveInstances (TypeSlot<TY>::get());
}
} // namespace lib
#endif /*LIB_ALLOCATION_CLUSTER_H*/

View file

@ -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<Num<3>> (4,5);
elements.emplace<Num<6>> (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<Num<1>>());
CHECK (1 == allocator.count<Num<3>>());
CHECK (1 == allocator.count<Num<6>>());
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

View file

@ -81689,6 +81689,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1715782747520" ID="ID_1770179360" MODIFIED="1715782759542" TEXT="node-basic-test: OK"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715782790810" ID="ID_1391251855" MODIFIED="1715782799529" TEXT="linked-elements-test: ">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1715815439855" ID="ID_1456163487" MODIFIED="1715815486736" TEXT="sollte LinkedElements umstellen auf Standard-konformen Allocator"/>
<node CREATED="1715815513228" ID="ID_1909349257" MODIFIED="1715815549523" TEXT="die NoOwnership-Policy wird dann zu einem speziellen Allocator"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715782815501" ID="ID_845459708" MODIFIED="1715782818814" TEXT="Nodefactory">
<icon BUILTIN="flag-yellow"/>
@ -81941,8 +81943,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1680563460649" ID="ID_127710483" MODIFIED="1715624384997" TEXT="MemManagement">
<arrowlink COLOR="#454059" DESTINATION="ID_1595450559" ENDARROW="Default" ENDINCLINATION="-1036;-77;" ID="Arrow_ID_1704085390" STARTARROW="None" STARTINCLINATION="-161;430;"/>
<node CREATED="1715623566743" ID="ID_688601712" MODIFIED="1715624494222" TEXT="AllocationCluster">
<linktarget COLOR="#816f7b" DESTINATION="ID_688601712" ENDARROW="Default" ENDINCLINATION="95;-321;" ID="Arrow_ID_859179840" SOURCE="ID_303611553" STARTARROW="None" STARTINCLINATION="-264;24;"/>
<linktarget COLOR="#816f7b" DESTINATION="ID_688601712" ENDARROW="Default" ENDINCLINATION="95;-321;" ID="Arrow_ID_1440452458" SOURCE="ID_1219678116" STARTARROW="None" STARTINCLINATION="-521;38;"/>
<linktarget COLOR="#816f7b" DESTINATION="ID_688601712" ENDARROW="Default" ENDINCLINATION="95;-321;" ID="Arrow_ID_859179840" SOURCE="ID_303611553" STARTARROW="None" STARTINCLINATION="-264;24;"/>
<node CREATED="1715786334834" ID="ID_885525745" MODIFIED="1715786341860" TEXT="Design / Systematik">
<node CREATED="1715786372102" ID="ID_1591071302" MODIFIED="1715786382355" TEXT="Aufwand f&#xfc;r Memory-Management wird geb&#xfc;ndelt"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1715786522984" ID="ID_1870113951" MODIFIED="1715786580201" TEXT="Konflikt: Performance &#x27f7; Sicherheit">