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:
parent
13e22f315a
commit
e8d9dcd9bf
2 changed files with 112 additions and 27 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81661,8 +81661,7 @@ Date:   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:   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ür zu große Objekte">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1715820259913" ID="ID_13842300" MODIFIED="1716563038609" TEXT="Check für zu groß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="Überlauf: neuen Block belegen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -81714,8 +81716,7 @@ Date:   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:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
...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>
|
||||
</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:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
...da man sich diesen Code ohnehin nur anschaut, wenn man muß, 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:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715790315814" ID="ID_878890004" MODIFIED="1715790393998" TEXT="Lösung fü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ösung">
|
||||
<node CREATED="1715790397014" ID="ID_14966020" LINK="#ID_869337683" MODIFIED="1715790537195" TEXT="Ergebnis einer Konfliktlösung">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -81899,6 +81898,18 @@ Date:   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 ü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 Überlauf provozieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716552266490" ID="ID_34007851" MODIFIED="1716552275535" TEXT="weiteren Ü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ür Verwendung mit STL-Container">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue