Library: implement accounting of storage size

These diagnostics helpers must rely on low-level trickery,
since the implementation strives at avoiding unnecessary storage overhead.
Since `AllocationCluster` is move-only (for good reasons) and `StorageManager`
can not be constructed independently, a »backdoor« is created by
forced cast, relying on the known memory layout
This commit is contained in:
Fischlurch 2024-05-24 17:05:50 +02:00
parent 13e22f315a
commit e8d9dcd9bf
2 changed files with 112 additions and 27 deletions

View file

@ -26,6 +26,15 @@
** for the render engine model. Here, in the actual translation unit, the generic part
** of these functions is emitted, while the corresponding header provides a strictly
** typed front-end, based on templates, which forward to the implementation eventually.
** \par low-level trickery
** The StorageManager implementation exploits object layout knowledge in order to
** operate with the bare minimum of administrative overhead; notably the next allocation
** is always located _within_ the current extent and by assuming that the remaining size
** is tracked correctly, the start of the current extent can always be re-discovered;
** the sequence of extents is managed as a linked list, where the `next*` resides in the
** first »slot« within each Extent; this pointer is _dressed up_ (reinterpreted) as a
** lib::LinkedElements with a heap allocator, which ends up performing the actual
** allocation in blocks of EXTENT_SIZ.
*/
@ -34,7 +43,9 @@
#include "lib/util.hpp"
using util::unConst;
using util::isnil;
using std::byte;
namespace lib {
@ -49,7 +60,7 @@ namespace lib {
HeapAlloc heap;
void*
allocate (size_t bytes)
allocate (size_t bytes) ////////////////////////OOO obsolete ... find a way to relocate this as custom allocator within LinkedElements!!!
{
return Alloc::allocate (heap, bytes);
}
@ -95,12 +106,14 @@ namespace lib {
union ManagementView
{
Storage storage{};
Storage storage;
Extents extents;
};
ManagementView view_;
StorageManager() = delete; ///< @warning used as _overlay view_ only, never created
public:
static StorageManager&
access (AllocationCluster& clu)
@ -122,14 +135,37 @@ namespace lib {
view_.extents.clear();
}
size_t
determineExtentCnt() ///< @warning finalises current block
{
closeCurrentBlock();
return view_.extents.size();
}
size_t
calcAllocInCurrentBlock() const
{
ENSURE (STORAGE_SIZ >= view_.storage.rest);
return STORAGE_SIZ - view_.storage.rest;
}
static auto determineStorageSize (AllocationCluster const&);
private:
byte*
getCurrentBlockStart() const
{
return static_cast<byte*>(view_.storage.pos)
+ view_.storage.rest
- EXTENT_SIZ;
}
void
closeCurrentBlock()
{
ASSERT (view_.storage.pos);
if (not view_.storage.pos) return;
// relocate the pos-pointer to the start of the block
view_.storage.pos = static_cast<std::byte*>(view_.storage.pos)
+ view_.storage.rest - EXTENT_SIZ;
view_.storage.pos = getCurrentBlockStart();
view_.storage.rest = 0;
}
@ -144,19 +180,25 @@ namespace lib {
/** creating a new AllocationCluster prepares a table capable
* of holding the individual object families to come. Each of those
* is managed by a separate instance of the low-level memory manager.
/**
* Prepare a new clustered allocation to be expanded by extents of size
* EXTENT_SIZ, yet discarded all at once when the dtor is called.
* The constructor does not allocate anything immediately.
*/
AllocationCluster::AllocationCluster()
: storage_{}
{
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.
/**
* On shutdown of the AllocationCluster walks all extents and invokes all
* registered deleter functions and then discards the complete storage.
* @note it is possible to allocate objects as _disposable_ meaning
* that no destructors will be enrolled and called for such objects.
*/
AllocationCluster::~AllocationCluster() noexcept
try
@ -177,7 +219,6 @@ namespace lib {
AllocationCluster::expandStorage (size_t allocRequest)
{
ENSURE (allocRequest + OVERHEAD <= EXTENT_SIZ);
void* newBlock = allocate (EXTENT_SIZ); ////////////////////////OOO obsolete ... use ManagementView instead!!!
StorageManager::access(*this).addBlock();
}
@ -188,19 +229,52 @@ namespace lib {
bool
AllocationCluster::_is_within_limits (size_t size, size_t align)
{
UNIMPLEMENTED ("size limits"); ///////////////////////////OOO enforce maximum size limits
auto isPower2 = [](size_t n){ return !(n & (n-1)); };
return 0 < size
and 0 < align
and size <= STORAGE_SIZ
and align <= STORAGE_SIZ
and isPower2 (align);
}
/** @internal diagnostics helper for unit testing.
* Creates a shallow copy of the management data and then locates
* the start of the current extent to determine the allocation size
* @return a pair (extent-count, bytes-in-last-extent)
*/
inline auto
AllocationCluster::StorageManager::determineStorageSize (AllocationCluster const& o)
{
size_t extents{0}, bytes{0};
if (o.storage_.pos)
{ // must manipulate pos pointer to find out count
Storage shallowCopy{o.storage_};
StorageManager& access = reinterpret_cast<StorageManager&> (shallowCopy);
bytes = access.calcAllocInCurrentBlock();
extents = access.determineExtentCnt();
}
return std::make_pair (extents, bytes);
}
size_t
AllocationCluster::numExtents() const
{
UNIMPLEMENTED ("Allocation management");
return StorageManager::determineStorageSize(*this).first;
}
/**
* @warning whenever there are more than one extent,
* the returned byte count is guessed only (upper bound), since
* actually allocated size is not tracked to save some overhead.
*/
size_t
AllocationCluster::numBytes() const
{
UNIMPLEMENTED ("Allocation management");
auto [extents,bytes] = StorageManager::determineStorageSize(*this);
return extents? bytes + (extents - 1) * STORAGE_SIZ
: 0;
}

View file

@ -81661,8 +81661,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
alignof()-Operator und die Hilfsfunktion std::align()
</p>
</body>
</html>
</richcontent>
</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"/>
@ -81696,8 +81695,11 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1715820275925" ID="ID_1184193638" MODIFIED="1715820287810" TEXT="Standardfall: Speicher belegen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715820259913" ID="ID_13842300" MODIFIED="1715820269104" TEXT="Check f&#xfc;r zu gro&#xdf;e Objekte">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1715820259913" ID="ID_13842300" MODIFIED="1716563038609" TEXT="Check f&#xfc;r zu gro&#xdf;e Objekte">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1716563047722" ID="ID_848423419" MODIFIED="1716563055539" TEXT="Storage-Diagnose">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1715820289221" ID="ID_1541316374" MODIFIED="1716130701433" TEXT="&#xdc;berlauf: neuen Block belegen">
<icon BUILTIN="button_ok"/>
@ -81714,8 +81716,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
auch: eine gradzahlige Anzahl an Overhead-Slots
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1715991799166" ID="ID_1984378311" MODIFIED="1715991936313" TEXT="Trick: aktuellen Block-Start rekonstruieren">
<richcontent TYPE="NOTE"><html>
@ -81725,8 +81726,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...durch diesen Trick sparen wir uns einen zus&#228;tzlichen Pointer auf den aktuellen Block: da std::allign(pos,rest) den pos-Zeiger stets koh&#228;rent zusammen mit dem rest-cnt manipuliert, k&#246;nnen wir stets aus beiden zusammen wieder zum Anfang des Blocks zur&#252;ckfinden.
</p>
</body>
</html>
</richcontent>
</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">
@ -81790,8 +81790,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...da man sich diesen Code ohnehin nur anschaut, wenn man mu&#223;, ist nichts mehr gewonnen, weitere Details nochmal durch eine Indirektion zu verbergen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1716130557432" ID="ID_1302192320" MODIFIED="1716130574360" TEXT="Layout-Trick: der Destructor-Descriptor liegt direkt vor dem Objekt">
@ -81837,7 +81836,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715790315814" ID="ID_878890004" MODIFIED="1715790393998" TEXT="L&#xf6;sung f&#xfc;r die Destruktoren schaffen">
<linktarget COLOR="#af578d" DESTINATION="ID_878890004" ENDARROW="Default" ENDINCLINATION="-446;33;" ID="Arrow_ID_464677545" SOURCE="ID_412040509" STARTARROW="None" STARTINCLINATION="-568;-24;"/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1715790397014" ID="ID_14966020" MODIFIED="1715790537195" TEXT="Ergebnis einer Konfliktl&#xf6;sung">
<node CREATED="1715790397014" ID="ID_14966020" LINK="#ID_869337683" MODIFIED="1715790537195" TEXT="Ergebnis einer Konfliktl&#xf6;sung">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -81899,6 +81898,18 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<arrowlink COLOR="#4173be" DESTINATION="ID_1783945506" ENDARROW="Default" ENDINCLINATION="204;17;" ID="Arrow_ID_1547348126" STARTARROW="None" STARTINCLINATION="292;-22;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1716552160019" ID="ID_146077309" MODIFIED="1716552282949" TEXT="die Mechanik &#xfc;berhaupt erst mal zum Laufen bekommen">
<icon BUILTIN="pencil"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716552180480" ID="ID_1382140920" MODIFIED="1716552189738" TEXT="eine Allokation im bestehenden Extent">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716552190584" ID="ID_1042988047" MODIFIED="1716552206544" TEXT="einen &#xdc;berlauf provozieren">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716552266490" ID="ID_34007851" MODIFIED="1716552275535" TEXT="weiteren &#xdc;berlauf mit Alignment">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715782674401" ID="ID_1481008630" MODIFIED="1715782690591" TEXT="neue Tests f&#xfc;r Verwendung mit STL-Container">
<icon BUILTIN="flag-yellow"/>