Library: idea for an extent management mechanism
* this is pure old-style low-level trickery * using a layout trick, the `AllocationCluster` can be operated with the bare minimum of overhead * this trick relies on the memory layout of `lib::LinkedElements`
This commit is contained in:
parent
c623298ac8
commit
72aea53ac3
3 changed files with 206 additions and 19 deletions
|
|
@ -30,14 +30,110 @@
|
|||
|
||||
|
||||
#include "lib/allocation-cluster.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/linked-elements.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
using util::isnil;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace {// Configuration constants
|
||||
const size_t EXTENT_SIZ = 256;
|
||||
const size_t OVERHEAD = 2 * sizeof(void*);
|
||||
const size_t STORAGE_SIZ = EXTENT_SIZ - OVERHEAD;
|
||||
|
||||
using HeapAlloc = std::allocator<std::byte>;
|
||||
using Alloc = std::allocator_traits<HeapAlloc>;
|
||||
|
||||
HeapAlloc heap;
|
||||
|
||||
void*
|
||||
allocate (size_t bytes)
|
||||
{
|
||||
return Alloc::allocate (heap, bytes);
|
||||
}
|
||||
|
||||
void
|
||||
destroy (void* storage)
|
||||
{
|
||||
Alloc::destroy (heap, static_cast<std::byte *> (storage));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An _overlay view_ for the AllocationCluster to add functionality
|
||||
* for adding / clearing extents and registering optional deleter functions.
|
||||
* @warning this is a tricky construct to operate each Allocation Cluster
|
||||
* with the absolute necessary minimum of organisational overhead.
|
||||
* The key point to note is that StorageManager is layout compatible
|
||||
* with AllocationCluster itself — achieved through use of the union
|
||||
* ManagementView, which holds a Storage descriptor member, but
|
||||
* also an alternate view to manage a chain of extents as
|
||||
* intrusive linked list (lib::LinkedElements).
|
||||
* @remark this trick relies on `std::align(pos,rest)` to manage the storage
|
||||
* coordinates coherently, allowing to re-establish the begin
|
||||
* of each storage block always, using pointer arithmetics.
|
||||
*/
|
||||
class AllocationCluster::StorageManager
|
||||
{
|
||||
struct Destructor
|
||||
{
|
||||
Destructor* next;
|
||||
///////////////////////////////////////////////OOO store deleter function here
|
||||
};
|
||||
using Destructors = lib::LinkedElements<Destructor>;
|
||||
|
||||
struct Extent
|
||||
{
|
||||
Extent* next;
|
||||
Destructors dtors;
|
||||
std::byte storage[STORAGE_SIZ];
|
||||
};
|
||||
using Extents = lib::LinkedElements<Extent>;
|
||||
|
||||
union ManagementView
|
||||
{
|
||||
Storage storage{};
|
||||
Extents extents;
|
||||
};
|
||||
|
||||
ManagementView view_;
|
||||
|
||||
public:
|
||||
static StorageManager&
|
||||
access (AllocationCluster& clu)
|
||||
{
|
||||
return reinterpret_cast<StorageManager&> (clu);
|
||||
}
|
||||
|
||||
void
|
||||
addBlock()
|
||||
{
|
||||
closeCurrentBlock();
|
||||
prependNextBlock();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
closeCurrentBlock()
|
||||
{
|
||||
ASSERT (view_.storage.pos);
|
||||
// relocate the pos-pointer to the start of the block
|
||||
view_.storage.pos += view_.storage.rest - EXTENT_SIZ;
|
||||
view_.storage.rest = 0;
|
||||
}
|
||||
|
||||
void
|
||||
prependNextBlock()
|
||||
{
|
||||
view_.extents.emplace();
|
||||
view_.storage.pos = & view_.extents.top().storage;
|
||||
view_.storage.rest = STORAGE_SIZ;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -46,30 +142,49 @@ namespace lib {
|
|||
* is managed by a separate instance of the low-level memory manager.
|
||||
*/
|
||||
AllocationCluster::AllocationCluster()
|
||||
{
|
||||
TRACE (memory, "new AllocationCluster");
|
||||
}
|
||||
{
|
||||
TRACE (memory, "new AllocationCluster");
|
||||
}
|
||||
|
||||
|
||||
/** On shutdown of the AllocationCluster we need to assure a certain
|
||||
* destruction order is maintained by explicitly invoking a cleanup
|
||||
* operation on each of the low-level memory manager objects.
|
||||
* operation on each of the low-level memory manager objects.
|
||||
*/
|
||||
AllocationCluster::~AllocationCluster() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
TRACE (memory, "shutting down AllocationCluster");
|
||||
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (progress, "discarding AllocationCluster")
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
TRACE (memory, "shutting down AllocationCluster");
|
||||
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (progress, "discarding AllocationCluster")
|
||||
|
||||
|
||||
/**
|
||||
* Expand the alloted storage pool by a block,
|
||||
* suitable to accommodate at least the indicated request.
|
||||
* @remark Storage blocks are organised as linked list,
|
||||
* allowing to de-allocate all blocks together.
|
||||
*/
|
||||
void
|
||||
AllocationCluster::expandStorage (size_t allocRequest)
|
||||
{
|
||||
ENSURE (allocRequest + OVERHEAD <= EXTENT_SIZ);
|
||||
void* newBlock = allocate (EXTENT_SIZ); ////////////////////////OOO obsolete ... use ManagementView instead!!!
|
||||
StorageManager::access(*this).addBlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === diagnostics helpers === */
|
||||
|
||||
bool
|
||||
AllocationCluster::_is_within_limits (size_t size, size_t align)
|
||||
{
|
||||
UNIMPLEMENTED ("size limits"); ///////////////////////////OOO enforce maximum size limits
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
|
|
|
|||
|
|
@ -97,9 +97,15 @@ namespace lib {
|
|||
AllocationCluster* mother_;
|
||||
};
|
||||
|
||||
/* maintaining the Allocation */
|
||||
void* storage_;
|
||||
size_t remain_;
|
||||
class StorageManager;
|
||||
|
||||
/** maintaining the Allocation */
|
||||
struct Storage
|
||||
{
|
||||
void* pos{nullptr};
|
||||
size_t rest{0};
|
||||
};
|
||||
Storage storage_;
|
||||
|
||||
public:
|
||||
AllocationCluster ();
|
||||
|
|
@ -144,10 +150,12 @@ namespace lib {
|
|||
void*
|
||||
allotMemory (size_t bytes, size_t alignment)
|
||||
{
|
||||
void* loc = std::align(alignment, bytes, storage_, remain_);
|
||||
ENSURE (_is_within_limits (bytes, alignment));
|
||||
void* loc = std::align (alignment, bytes, storage_.pos, storage_.rest);
|
||||
if (loc)
|
||||
return loc;
|
||||
UNIMPLEMENTED ("actual memory management");
|
||||
expandStorage (bytes);
|
||||
return allotMemory (bytes, alignment);
|
||||
///////////////////////////////////////////////////////////OOO claim next macro block
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +165,9 @@ namespace lib {
|
|||
{
|
||||
return static_cast<X*> (allotMemory (cnt * sizeof(X), alignof(X)));
|
||||
}
|
||||
|
||||
void expandStorage (size_t);
|
||||
bool _is_within_limits (size_t,size_t);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81697,6 +81697,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715818949821" ID="ID_1747630681" MODIFIED="1715818962407" TEXT="Thema: Alignment">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1715985108575" ID="ID_1415977694" MODIFIED="1715988173554" TEXT="das kann man der Standard-Lib überlassen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
alignof()-Operator und die Hilfsfunktion std::align()
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1715818964036" ID="ID_1917864530" MODIFIED="1715818999461" TEXT="bedeutet: der Offset wird justiert bis er auf einer Alignment-Grenze liegt"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715819022774" ID="ID_541145684" MODIFIED="1715819026470" TEXT="Anforderungen">
|
||||
|
|
@ -81730,6 +81744,53 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715820289221" ID="ID_1541316374" MODIFIED="1715820300780" TEXT="Überlauf: neuen Block belegen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1715990297583" ID="ID_389684421" MODIFIED="1715990309548" TEXT="direkt per Standard-Allocator belegen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715990310261" ID="ID_1768477879" MODIFIED="1715990332985" TEXT="Idee: stattdessen virtuell eine Extent-Struktur mit Linked-Elements erzeugen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1715991737062" ID="ID_690246579" MODIFIED="1715991797332" TEXT="Zweck: absolutes Minimum an Overhead">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
auch: eine gradzahlige Anzahl an Overhead-Slots
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1715991799166" ID="ID_1984378311" MODIFIED="1715991936313" TEXT="Trick: aktuellen Block-Start rekonstruieren">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...durch diesen Trick sparen wir uns einen zusätzlichen Pointer auf den aktuellen Block: da std::allign(pos,rest) den pos-Zeiger stets kohärent zusammen mit dem rest-cnt manipuliert, können wir stets aus beiden zusammen wieder zum Anfang des Blocks zurückfinden.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1715991938427" ID="ID_1338480715" MODIFIED="1715991957260" TEXT="Mit einer Union lassen sich zwei verschiedene Arbeits-Modi realisieren">
|
||||
<node CREATED="1715991959415" ID="ID_143253913" MODIFIED="1715991963118" TEXT="Allokations-Modus">
|
||||
<node CREATED="1715991990569" ID="ID_1549411025" MODIFIED="1715992008134" TEXT="der Zeiger zeigt auf die nächste belegbare Zelle"/>
|
||||
<node CREATED="1715992008639" ID="ID_1788117937" MODIFIED="1715992025683" TEXT="dahinter steth noch ein size_t mit der verbleibenden Speichermenge"/>
|
||||
</node>
|
||||
<node CREATED="1715991963775" ID="ID_1604234308" MODIFIED="1715991968419" TEXT="Extent-Modus">
|
||||
<node CREATED="1715991970129" ID="ID_408389743" MODIFIED="1715991988683" TEXT="der Zeiger ist der top-Zeiger einer LinkedElements-List"/>
|
||||
<node CREATED="1715992043143" ID="ID_398369421" MODIFIED="1715992052497" TEXT="er zeigt also auf den Anfang des Extents"/>
|
||||
<node CREATED="1715992053205" ID="ID_670105438" MODIFIED="1715992066298" TEXT="direkt im Anfang des Extents liegt ein next-Zeiger für die Blockverkettung"/>
|
||||
<node CREATED="1715992069805" ID="ID_1072402772" MODIFIED="1715992084606" TEXT="damit kann weitere Funktionalität in der Extent-Strukgur implementiert werden"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1715992091622" ID="ID_73255844" MODIFIED="1715992116009" TEXT="ACHTUNG: vor dem Umschalten in den Extent-Modus: Zeiger auf Blockanfang zurücksetzen">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715820305436" ID="ID_1741026762" MODIFIED="1715820308396" TEXT="Clean-up">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue