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
|
** 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
|
** of these functions is emitted, while the corresponding header provides a strictly
|
||||||
** typed front-end, based on templates, which forward to the implementation eventually.
|
** 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"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
using util::unConst;
|
||||||
using util::isnil;
|
using util::isnil;
|
||||||
|
using std::byte;
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
|
@ -49,7 +60,7 @@ namespace lib {
|
||||||
HeapAlloc heap;
|
HeapAlloc heap;
|
||||||
|
|
||||||
void*
|
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);
|
return Alloc::allocate (heap, bytes);
|
||||||
}
|
}
|
||||||
|
|
@ -95,12 +106,14 @@ namespace lib {
|
||||||
|
|
||||||
union ManagementView
|
union ManagementView
|
||||||
{
|
{
|
||||||
Storage storage{};
|
Storage storage;
|
||||||
Extents extents;
|
Extents extents;
|
||||||
};
|
};
|
||||||
|
|
||||||
ManagementView view_;
|
ManagementView view_;
|
||||||
|
|
||||||
|
StorageManager() = delete; ///< @warning used as _overlay view_ only, never created
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static StorageManager&
|
static StorageManager&
|
||||||
access (AllocationCluster& clu)
|
access (AllocationCluster& clu)
|
||||||
|
|
@ -122,14 +135,37 @@ namespace lib {
|
||||||
view_.extents.clear();
|
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:
|
private:
|
||||||
|
byte*
|
||||||
|
getCurrentBlockStart() const
|
||||||
|
{
|
||||||
|
return static_cast<byte*>(view_.storage.pos)
|
||||||
|
+ view_.storage.rest
|
||||||
|
- EXTENT_SIZ;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
closeCurrentBlock()
|
closeCurrentBlock()
|
||||||
{
|
{
|
||||||
ASSERT (view_.storage.pos);
|
if (not view_.storage.pos) return;
|
||||||
// relocate the pos-pointer to the start of the block
|
// relocate the pos-pointer to the start of the block
|
||||||
view_.storage.pos = static_cast<std::byte*>(view_.storage.pos)
|
view_.storage.pos = getCurrentBlockStart();
|
||||||
+ view_.storage.rest - EXTENT_SIZ;
|
|
||||||
view_.storage.rest = 0;
|
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()
|
AllocationCluster::AllocationCluster()
|
||||||
|
: storage_{}
|
||||||
{
|
{
|
||||||
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
|
* On shutdown of the AllocationCluster walks all extents and invokes all
|
||||||
* operation on each of the low-level memory manager objects.
|
* 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
|
AllocationCluster::~AllocationCluster() noexcept
|
||||||
try
|
try
|
||||||
|
|
@ -177,7 +219,6 @@ namespace lib {
|
||||||
AllocationCluster::expandStorage (size_t allocRequest)
|
AllocationCluster::expandStorage (size_t allocRequest)
|
||||||
{
|
{
|
||||||
ENSURE (allocRequest + OVERHEAD <= EXTENT_SIZ);
|
ENSURE (allocRequest + OVERHEAD <= EXTENT_SIZ);
|
||||||
void* newBlock = allocate (EXTENT_SIZ); ////////////////////////OOO obsolete ... use ManagementView instead!!!
|
|
||||||
StorageManager::access(*this).addBlock();
|
StorageManager::access(*this).addBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,19 +229,52 @@ namespace lib {
|
||||||
bool
|
bool
|
||||||
AllocationCluster::_is_within_limits (size_t size, size_t align)
|
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
|
size_t
|
||||||
AllocationCluster::numExtents() const
|
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
|
size_t
|
||||||
AllocationCluster::numBytes() const
|
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()
|
alignof()-Operator und die Hilfsfunktion std::align()
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html></richcontent>
|
||||||
</richcontent>
|
|
||||||
<icon BUILTIN="idea"/>
|
<icon BUILTIN="idea"/>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1715818964036" ID="ID_1917864530" MODIFIED="1715818999461" TEXT="bedeutet: der Offset wird justiert bis er auf einer Alignment-Grenze liegt"/>
|
<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">
|
<node COLOR="#338800" CREATED="1715820275925" ID="ID_1184193638" MODIFIED="1715820287810" TEXT="Standardfall: Speicher belegen">
|
||||||
<icon BUILTIN="button_ok"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715820259913" ID="ID_13842300" MODIFIED="1715820269104" TEXT="Check für zu große Objekte">
|
<node COLOR="#338800" CREATED="1715820259913" ID="ID_13842300" MODIFIED="1716563038609" TEXT="Check für zu große Objekte">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#338800" CREATED="1716563047722" ID="ID_848423419" MODIFIED="1716563055539" TEXT="Storage-Diagnose">
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
</node>
|
</node>
|
||||||
<node COLOR="#338800" CREATED="1715820289221" ID="ID_1541316374" MODIFIED="1716130701433" TEXT="Überlauf: neuen Block belegen">
|
<node COLOR="#338800" CREATED="1715820289221" ID="ID_1541316374" MODIFIED="1716130701433" TEXT="Überlauf: neuen Block belegen">
|
||||||
<icon BUILTIN="button_ok"/>
|
<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
|
auch: eine gradzahlige Anzahl an Overhead-Slots
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html></richcontent>
|
||||||
</richcontent>
|
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1715991799166" ID="ID_1984378311" MODIFIED="1715991936313" TEXT="Trick: aktuellen Block-Start rekonstruieren">
|
<node CREATED="1715991799166" ID="ID_1984378311" MODIFIED="1715991936313" TEXT="Trick: aktuellen Block-Start rekonstruieren">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<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.
|
...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>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html></richcontent>
|
||||||
</richcontent>
|
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1715991938427" ID="ID_1338480715" MODIFIED="1715991957260" TEXT="Mit einer Union lassen sich zwei verschiedene Arbeits-Modi realisieren">
|
<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="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
|
...da man sich diesen Code ohnehin nur anschaut, wenn man muß, ist nichts mehr gewonnen, weitere Details nochmal durch eine Indirektion zu verbergen
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html></richcontent>
|
||||||
</richcontent>
|
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1716130557432" ID="ID_1302192320" MODIFIED="1716130574360" TEXT="Layout-Trick: der Destructor-Descriptor liegt direkt vor dem Objekt">
|
<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">
|
<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;"/>
|
<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"/>
|
<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>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head/>
|
<head/>
|
||||||
<body>
|
<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;"/>
|
<arrowlink COLOR="#4173be" DESTINATION="ID_1783945506" ENDARROW="Default" ENDINCLINATION="204;17;" ID="Arrow_ID_1547348126" STARTARROW="None" STARTINCLINATION="292;-22;"/>
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
</node>
|
</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>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715782674401" ID="ID_1481008630" MODIFIED="1715782690591" TEXT="neue Tests für Verwendung mit STL-Container">
|
<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"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue