BufferProvider: draft for the basic global allocator interface
This implementation and the accompanying test might seem pointless, yet the point is that the interface is aligned to the emerging concepts for memory allocators in Lumiera, while also directly delegating to the std::allocator for the time being. Before we're able to observe the actual allocator trend and behaviour, it is pretty much moot to engage into any kind of optimisation; the generic heap allocator performs quite well on recent C++ runtimes, and, furthermore, the most important performance tweak is to use a thread-local pool of recent allocations, which should allow our jobs to run without impeding each other.
This commit is contained in:
parent
76dbb980b5
commit
bf446626a8
6 changed files with 533 additions and 55 deletions
|
|
@ -20,6 +20,9 @@
|
|||
#ifndef LIB_UTIL_QUANT_H
|
||||
#define LIB_UTIL_QUANT_H
|
||||
|
||||
#include "lib/integral.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
|
|
@ -31,7 +34,7 @@ namespace util {
|
|||
|
||||
template<typename N>
|
||||
inline constexpr bool
|
||||
isPow2 (N n)
|
||||
isPow2 (N n) noexcept
|
||||
{
|
||||
return n > 0 and !(n & (n-1));
|
||||
}; // at each power of 2, a new bit is set for the first time
|
||||
|
|
@ -44,7 +47,8 @@ namespace util {
|
|||
I quot;
|
||||
I rem;
|
||||
|
||||
IDiv (I num, I den)
|
||||
constexpr
|
||||
IDiv (I num, I den) noexcept
|
||||
: quot(num/den)
|
||||
, rem(num - quot*den)
|
||||
{ }
|
||||
|
|
@ -54,7 +58,8 @@ namespace util {
|
|||
struct IDiv<int>
|
||||
: div_t
|
||||
{
|
||||
IDiv (int num, int den)
|
||||
constexpr
|
||||
IDiv (int num, int den) noexcept
|
||||
: div_t(div (num,den))
|
||||
{ }
|
||||
};
|
||||
|
|
@ -63,7 +68,8 @@ namespace util {
|
|||
struct IDiv<long>
|
||||
: ldiv_t
|
||||
{
|
||||
IDiv (long num, long den)
|
||||
constexpr
|
||||
IDiv (long num, long den) noexcept
|
||||
: ldiv_t(ldiv (num,den))
|
||||
{ }
|
||||
};
|
||||
|
|
@ -72,14 +78,15 @@ namespace util {
|
|||
struct IDiv<llong>
|
||||
: lldiv_t
|
||||
{
|
||||
IDiv (llong num, llong den)
|
||||
constexpr
|
||||
IDiv (llong num, llong den) noexcept
|
||||
: lldiv_t(lldiv (num,den))
|
||||
{ }
|
||||
};
|
||||
|
||||
template<typename I>
|
||||
inline IDiv<I>
|
||||
iDiv (I num, I den) ///< support type inference and auto typing...
|
||||
inline constexpr IDiv<I>
|
||||
iDiv (I num, I den) noexcept ///< support type inference and auto typing...
|
||||
{
|
||||
return IDiv<I>{num,den};
|
||||
}
|
||||
|
|
@ -93,8 +100,8 @@ namespace util {
|
|||
* @see UtilFloordiv_test
|
||||
*/
|
||||
template<typename I>
|
||||
inline I
|
||||
floordiv (I num, I den)
|
||||
inline constexpr I
|
||||
floordiv (I num, I den) noexcept
|
||||
{
|
||||
if (0 < (num^den))
|
||||
return num/den;
|
||||
|
|
@ -115,8 +122,8 @@ namespace util {
|
|||
* @see UtilFloorwarp_test
|
||||
*/
|
||||
template<typename I>
|
||||
inline IDiv<I>
|
||||
floorwrap (I num, I den)
|
||||
inline constexpr IDiv<I>
|
||||
floorwrap (I num, I den) noexcept
|
||||
{
|
||||
IDiv<I> res(num,den);
|
||||
if (0 > (num^den) and res.rem)
|
||||
|
|
@ -129,6 +136,23 @@ namespace util {
|
|||
}
|
||||
|
||||
|
||||
/** quantise towards ceiling.
|
||||
* This function models to fit something into a tiled structure.
|
||||
*/
|
||||
template<typename U> requires std::unsigned_integral<U>
|
||||
inline constexpr U
|
||||
ceilDiv (U num, U den) noexcept
|
||||
{
|
||||
return (num + den - 1u) / den;
|
||||
}
|
||||
|
||||
static_assert (0 == ceilDiv (0u,5u));
|
||||
static_assert (1 == ceilDiv (1u,5u));
|
||||
static_assert (1 == ceilDiv (4u,5u));
|
||||
static_assert (1 == ceilDiv (5u,5u));
|
||||
static_assert (2 == ceilDiv (6u,5u));
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* epsilon comparison of doubles.
|
||||
|
|
@ -146,8 +170,8 @@ namespace util {
|
|||
* @see https://en.wikipedia.org/wiki/Unit_in_the_last_place
|
||||
* @todo 3/2024 seems we have solved this problem several times meanwhile /////////////////////////////////TICKET #1360 sort out floating-point rounding and precision
|
||||
*/
|
||||
inline bool
|
||||
almostEqual (double d1, double d2, unsigned int ulp =2)
|
||||
inline constexpr bool
|
||||
almostEqual (double d1, double d2, unsigned int ulp =2) noexcept
|
||||
{
|
||||
using std::fabs;
|
||||
return fabs (d1-d2) < DBL_EPSILON * fabs (d1+d2) * ulp
|
||||
|
|
|
|||
|
|
@ -13,12 +13,30 @@
|
|||
|
||||
/** @file engine-buffer-manager.hpp
|
||||
** Central hub of buffer management within the Lumiera Render Engine.
|
||||
** Instantiated as global service and part of the Engine, this global
|
||||
** allocation coordinator is responsible to provide pre-allocated blocks
|
||||
** for render buffers. These allocations are categorised by their size,
|
||||
** and will be sent down to LocalMemPool instances running in each
|
||||
** worker thread. This involves asynchronous messaging to avoid
|
||||
** any blocking of work jobs due to central resource management.
|
||||
**
|
||||
** @todo WIP-WIP-WIP 4/2026
|
||||
** @todo WIP-WIP 4/2026 The intention is to use a family of pools eventually,
|
||||
** with some of the most relevant tile sizes. The EngineBufferManager
|
||||
** would then actually retain ownership of all allocations and just
|
||||
** _lease_ them to the LocalMemPool instances. Allocations would be
|
||||
** added in extents, likely also using some usage bitmap. However
|
||||
** building any such kind of optimised allocator requires to conduct
|
||||
** very detailed _and_ realistic performance observations; furthermore
|
||||
** it is likely that we'd need some dynamic scheme to select suitable
|
||||
** tile sizes and pool dimensions. Not sure if it is even possible
|
||||
** to improve performance by such a scheme; with some likelihood
|
||||
** the majority of the benefits will be reaped already just by
|
||||
** introducing thread-local pools without wasting too much memory.
|
||||
**
|
||||
** @see buffer-provider.hpp
|
||||
** @see EnginePufferManager_test
|
||||
** @see LocalBufferStore_test
|
||||
** @see BufferProviderProtocol_test
|
||||
** @see buffer-provider.hpp
|
||||
*/
|
||||
|
||||
#ifndef VAULT_MEM_ENGINE_BUFFR_MANAGA_H
|
||||
|
|
@ -26,43 +44,120 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/hash-value.h"
|
||||
#include "include/logging.h"
|
||||
#include "lib/util-quant.hpp"
|
||||
#include "lib/allocator-handle.hpp"
|
||||
//#include "lib/hash-value.h"
|
||||
//#include "include/logging.h"
|
||||
#include "vault/mem/buffhandle.hpp"
|
||||
#include "vault/mem/type-handler.hpp"
|
||||
#include "vault/mem/buffer-local-tag.hpp"
|
||||
//#include "vault/mem/type-handler.hpp"
|
||||
//#include "vault/mem/buffer-local-tag.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/util.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace vault{
|
||||
namespace mem {
|
||||
|
||||
using lib::HashVal;
|
||||
using util::unConst;
|
||||
// using lib::HashVal;
|
||||
// using util::unConst;
|
||||
|
||||
namespace err = lumiera::error;
|
||||
|
||||
|
||||
using std::byte;
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
||||
|
||||
namespace { // internal blah
|
||||
namespace { // config and internal helpers...
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === deaf === */
|
||||
/* === Setup of »naïve« buffer allocation === */
|
||||
|
||||
/**
|
||||
* A placeholder definition for the real buffer allocator:
|
||||
* - allocates buffers aligned to a fixed #TILE_SIZ
|
||||
* - passes any allocation request directly to the heap allocator
|
||||
* - performs no state tracking and cleanup whatsoever.
|
||||
*/
|
||||
struct HeapBufferAllocator
|
||||
{
|
||||
static constexpr size_t TILE_SIZ = 64;
|
||||
using BaseTile = std::array<byte, TILE_SIZ>;
|
||||
|
||||
using Allo = std::allocator<BaseTile>;
|
||||
using AlloT = std::allocator_traits<Allo>;
|
||||
|
||||
static_assert (util::isPow2 (sizeof(BaseTile)));
|
||||
static_assert (lib::allo::is_Stateless_v<Allo>);
|
||||
|
||||
|
||||
struct Alloc
|
||||
{
|
||||
Buff* mem;
|
||||
size_t siz;
|
||||
};
|
||||
|
||||
static constexpr size_t
|
||||
numTiles (size_t sizRequest)
|
||||
{
|
||||
return util::ceilDiv (sizRequest, sizeof(BaseTile));
|
||||
}
|
||||
|
||||
static Buff*
|
||||
mark_as_Buffer (void* mem)
|
||||
{
|
||||
return static_cast<Buff*> (mem);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate continuous memory block large enough to fit.
|
||||
* @throws std::bad_alloc when allocation fails
|
||||
* @note client is responsible to \ref deallocate()
|
||||
*/
|
||||
Alloc
|
||||
allocate (size_t sizRequest)
|
||||
{
|
||||
Allo allo;
|
||||
auto cnt = numTiles (sizRequest);
|
||||
auto loc = AlloT::allocate (allo, cnt);
|
||||
return {mark_as_Buffer(loc)
|
||||
,cnt * TILE_SIZ
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocates a memory block that was previously
|
||||
* [allocated](\ref allocate()) by this buffer allocator.
|
||||
*/
|
||||
void
|
||||
deallocate (Alloc alloc)
|
||||
{
|
||||
Allo allo;
|
||||
auto cnt = numTiles (alloc.siz);
|
||||
auto mem = reinterpret_cast<BaseTile*> (alloc.mem);
|
||||
AlloT::deallocate (allo, mem, cnt);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************//**
|
||||
* Core service of the Render Engine : global buffer manager.
|
||||
*/
|
||||
class EngineBufferManager
|
||||
{
|
||||
public:
|
||||
using BufferAllocator = HeapBufferAllocator;
|
||||
};
|
||||
|
||||
|
||||
}} // namespace vault::mem
|
||||
#endif /*VAULT_MEM_ENGINE_BUFFR_MANAGA_H*/
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/integral.hpp"
|
||||
#include "lib/util-quant.hpp"
|
||||
#include "vault/mem/buffer-metadata.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "lib/format-obj.hpp"
|
||||
|
|
@ -50,16 +51,10 @@ namespace mem {
|
|||
using StorageWord = uint64_t;
|
||||
constexpr inline size_t WORD_SIZ = sizeof(StorageWord);
|
||||
|
||||
constexpr size_t
|
||||
ceilDiv (size_t num, size_t den) noexcept
|
||||
{
|
||||
return (num + den - 1u) / den;
|
||||
}
|
||||
|
||||
constexpr size_t
|
||||
wordCnt (size_t sizeRequest) noexcept
|
||||
{
|
||||
return ceilDiv (sizeRequest, WORD_SIZ);
|
||||
return util::ceilDiv (sizeRequest, WORD_SIZ);
|
||||
}
|
||||
|
||||
static_assert (0 == wordCnt(0));
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "Engine Buffer-Manager interface" EngineBufferManager_test <<END
|
||||
END
|
||||
|
||||
|
||||
PLANNED "Engine Interface basics" EngineInterface_test <<END
|
||||
END
|
||||
|
||||
|
|
|
|||
131
tests/vault/mem/engine-buffer-manager-test.cpp
Normal file
131
tests/vault/mem/engine-buffer-manager-test.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
EngineBufferManager(Test) - cover the interface to a central buffer manager
|
||||
|
||||
Copyright (C)
|
||||
2026, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See the file COPYING for further details.
|
||||
|
||||
* *****************************************************************/
|
||||
|
||||
/** @file engine-buffer-manager-test.cpp
|
||||
** unit test \ref EngineBufferManager_test
|
||||
*/
|
||||
|
||||
|
||||
#include "test/run.hpp"
|
||||
#include "test/test-helper.hpp"
|
||||
//#include "vault/mem/engine-buffer-metadata.hpp"
|
||||
#include "vault/mem/engine-buffer-manager.hpp"
|
||||
//#include "lib/depend-inject.hpp"
|
||||
//#include "lib/thread.hpp"
|
||||
//#include "lib/error.hpp"
|
||||
#include "test/diagnostic-output.hpp"////////////TODO
|
||||
|
||||
|
||||
//using std::this_thread::yield;
|
||||
//using lib::Thread;
|
||||
|
||||
namespace vault {
|
||||
namespace mem {
|
||||
namespace test {
|
||||
|
||||
// using LERR_(LOGIC);
|
||||
// using LERR_(LIFECYCLE);
|
||||
|
||||
|
||||
namespace { // Test helper
|
||||
|
||||
// template<typename X>
|
||||
// Buff*
|
||||
// mark_as_Buffer(X& something)
|
||||
// {
|
||||
// return reinterpret_cast<Buff*> (std::addressof(something));
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************//**
|
||||
* @test Show how the central buffer manager of the Render Engine can be accessed.
|
||||
*/
|
||||
class EngineBufferManager_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
// seedRand();
|
||||
|
||||
demonstrate_AllocatorInterface();
|
||||
verify_DataTransfer();
|
||||
verify_lulz();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test show how to allocate buffer memory
|
||||
* with the underlying base allocator.
|
||||
* @remark 4/2026 for the first integration of the Render Engine,
|
||||
* a simplified implementation is used that just delegates
|
||||
* to the regular heap allocator (which actually performs
|
||||
* quite well on modern C++ STDLib). The plan is to
|
||||
* use some kind of tiling pool allocator eventually,
|
||||
* contingent on the actual performance observations.
|
||||
*/
|
||||
void
|
||||
demonstrate_AllocatorInterface()
|
||||
{
|
||||
using Allo = EngineBufferManager::BufferAllocator;
|
||||
CHECK (lib::allo::is_Stateless_v<Allo>);
|
||||
|
||||
Allo allo;
|
||||
auto a1 = allo.allocate (55);
|
||||
auto a2 = allo.allocate (555);
|
||||
CHECK (a1.mem);
|
||||
CHECK (a2.mem);
|
||||
CHECK (a1.siz == Allo::TILE_SIZ);
|
||||
CHECK (a2.siz % Allo::TILE_SIZ == 0);
|
||||
CHECK (a2.siz > 555);
|
||||
|
||||
// can use that memory without blowing up....
|
||||
auto doooh = new(a1.mem) f128{std::numeric_limits<f128>::max()};
|
||||
*doooh /= 2;
|
||||
CHECK (2 == std::numeric_limits<f128>::max() / *doooh);
|
||||
|
||||
allo.deallocate (a1);
|
||||
allo.deallocate (a2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test */
|
||||
void
|
||||
verify_DataTransfer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test
|
||||
*/
|
||||
void
|
||||
verify_lulz()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (EngineBufferManager_test, "unit engine");
|
||||
|
||||
|
||||
|
||||
}}} // namespace vault::mem::test
|
||||
|
|
@ -74191,8 +74191,9 @@
|
|||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1716728664652" ID="ID_1644685634" MODIFIED="1776730151948" TEXT="Strukturen">
|
||||
<node CREATED="1716728664652" ID="ID_1644685634" MODIFIED="1777328614629" TEXT="Strukturen">
|
||||
<linktarget COLOR="#5c6c7a" DESTINATION="ID_1644685634" ENDARROW="Default" ENDINCLINATION="-892;2424;" ID="Arrow_ID_284707831" SOURCE="ID_622928577" STARTARROW="None" STARTINCLINATION="-1922;116;"/>
|
||||
<linktarget COLOR="#345274" DESTINATION="ID_1644685634" ENDARROW="Default" ENDINCLINATION="-529;1146;" ID="Arrow_ID_491300253" SOURCE="ID_1944871660" STARTARROW="None" STARTINCLINATION="-2513;119;"/>
|
||||
<node CREATED="1716728667779" ID="ID_467995115" MODIFIED="1716728670092" TEXT="Namen">
|
||||
<node CREATED="1716728670955" ID="ID_1816165091" MODIFIED="1716728689109">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -74481,7 +74482,7 @@
|
|||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#46418e" CREATED="1696538113039" ID="ID_314350695" MODIFIED="1696538164825" TEXT="#1279 : Beschluß(9/23) ⟹ auf C++ Standardlib aufgebaut">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1696538113039" MODIFIED="1696538113039" TEXT="früher gab es einen in C implementieren Threadpool-Service"/>
|
||||
<node CREATED="1696538113039" MODIFIED="1696538113039" TEXT="im Zuge »Playback Vertical Slice« wurde Anforderungs-Analyse gemacht"/>
|
||||
<node CREATED="1696538113039" ID="ID_49590855" MODIFIED="1696538113039" TEXT="im Zuge »Playback Vertical Slice« wurde Anforderungs-Analyse gemacht"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1695597011139" FOLDED="true" ID="ID_956380031" MODIFIED="1696538202836" TEXT="Neues Design Thread-Wrapper">
|
||||
<linktarget COLOR="#499ca8" DESTINATION="ID_956380031" ENDARROW="Default" ENDINCLINATION="-1261;104;" ID="Arrow_ID_482806479" SOURCE="ID_125138411" STARTARROW="None" STARTINCLINATION="-1063;-42;"/>
|
||||
|
|
@ -145856,7 +145857,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1777050440887" ID="ID_1006627416" MODIFIED="1777066692859" TEXT="Verhalten spezifizieren">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1777050520227" ID="ID_796153621" MODIFIED="1777050524094" TEXT="Use-Cases">
|
||||
<node CREATED="1777050520227" ID="ID_796153621" MODIFIED="1777325912544" TEXT="Use-Cases">
|
||||
<linktarget COLOR="#36506f" DESTINATION="ID_796153621" ENDARROW="Default" ENDINCLINATION="-452;0;" ID="Arrow_ID_1304449539" SOURCE="ID_1675354420" STARTARROW="Default" STARTINCLINATION="-32;-602;"/>
|
||||
<node CREATED="1777068198295" ID="ID_1010651012" MODIFIED="1777068390012" TEXT="Allokationsbedarf ankündigen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
|
|
@ -146091,10 +146093,191 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819109351" ID="ID_1998897998" MODIFIED="1776819155584" TEXT="EngineBufferManager">
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1776819109351" ID="ID_1998897998" MODIFIED="1777339324992" TEXT="EngineBufferManager">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819156872" ID="ID_1476925601" MODIFIED="1776819956355" TEXT="Grundkonzeption schaffen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1776819156872" ID="ID_1476925601" MODIFIED="1777339332224" TEXT="Grundkonzeption schaffen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1777325396485" ID="ID_947969066" MODIFIED="1777325400377" TEXT="Interface">
|
||||
<node CREATED="1777325407524" ID="ID_1333052454" MODIFIED="1777325421966" TEXT="Zugang via lib::Depend<EngineBufferManager>">
|
||||
<node CREATED="1777325446047" ID="ID_1500798718" MODIFIED="1777325451218" TEXT="geht das überhaupt?"/>
|
||||
<node CREATED="1777325452127" ID="ID_1999785214" MODIFIED="1777325463483" TEXT="müßte nämlich default-konstruierbar sein"/>
|
||||
<node CREATED="1777325557816" ID="ID_1748588651" MODIFIED="1777325642908" TEXT="wird hier jemals ein »Default-Setup« gebraucht?">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...das wäre nur der Fall, wenn ein Unit-Test „mal eben“ eine Render-Engine braucht. Denn sonst decken wir den Default-Fall bereits mit dem NaiveBufferSetup ab
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
<node CREATED="1777325501367" ID="ID_586279034" MODIFIED="1777325521405" TEXT="tatsächlich wollen wir hier eine explizit gemanagete Server-Komponente"/>
|
||||
<node CREATED="1777325699181" ID="ID_1392466705" MODIFIED="1777325832394" TEXT="Dependency-Factory kann auf abstrakte Basisklasse konfiguriert werdn">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Und dann würde jeder Zugriff mit einer Exception scheitern....
|
||||
</p>
|
||||
<p>
|
||||
So lange, bis man den statische Aufruf tätigt: 
|
||||
</p>
|
||||
<p>
|
||||
<font color="#635e96" face="DejaVu Sans Mono" size="9pt"><b>DependInject</b></font><font color="#65533c" face="DejaVu Sans Mono" size="9pt"><</font><font color="#635e96" face="DejaVu Sans Mono" size="9pt"><b>Base</b></font><font color="#65533c" face="DejaVu Sans Mono" size="9pt">>::</font><font color="#61143c" face="DejaVu Sans Mono" size="9pt"><i>useSingleton<Sub></i></font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1777325872302" ID="ID_1675354420" MODIFIED="1777325912544" TEXT="Use-Cases">
|
||||
<arrowlink COLOR="#36506f" DESTINATION="ID_796153621" ENDARROW="Default" ENDINCLINATION="-452;0;" ID="Arrow_ID_1304449539" STARTARROW="Default" STARTINCLINATION="-32;-602;"/>
|
||||
<node CREATED="1777326001051" ID="ID_825262980" MODIFIED="1777326053002" TEXT="Allokation(en) asynchron beauftragen"/>
|
||||
<node CREATED="1777326037424" ID="ID_613961876" MODIFIED="1777326058745" TEXT="Allokation(en) synchron beauftragen"/>
|
||||
<node CREATED="1777326109630" ID="ID_1521192845" MODIFIED="1777326120671" TEXT="Allokation asynchron zurückgeben"/>
|
||||
<node CREATED="1777326206909" ID="ID_421293619" MODIFIED="1777326210833" TEXT="Allokator-Steuerung">
|
||||
<node CREATED="1777326212094" ID="ID_1130719488" MODIFIED="1777326215708" TEXT="Allokation"/>
|
||||
<node CREATED="1777326216481" ID="ID_1774862481" MODIFIED="1777326219716" TEXT="Deallokation"/>
|
||||
<node CREATED="1777326220702" ID="ID_911135656" MODIFIED="1777326226035" TEXT="Kapazitätsmanagement"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1777326654102" ID="ID_499885447" MODIFIED="1777326656831" TEXT="Struktur">
|
||||
<node CREATED="1777326657789" ID="ID_831464061" MODIFIED="1777326715033" TEXT="Längerfristig: Policy-based-Design">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Schon allein um die konfigurierbaren Aspekte klar herauszustellen, und um ggfs. Quellen dynamischer Konfiguration zu verdrahten
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1777326753646" ID="ID_448684473" MODIFIED="1777326779620" TEXT="doch zunächst einmal: alles direkt coden">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1777339002955" ID="ID_758544304" MODIFIED="1777339007206" TEXT="typedef BufferAllocator"/>
|
||||
<node CREATED="1777339008730" ID="ID_1534654283" MODIFIED="1777339172707" TEXT="verwende fix eine Tile-Size von 64 Bytes">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...das ist die übliche Cache-Line-Size auf x86_64
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1777339178309" ID="ID_142350391" MODIFIED="1777339187028" TEXT="verdrahte direkt auf std::allocator"/>
|
||||
</node>
|
||||
<node CREATED="1777326795907" ID="ID_1470622255" MODIFIED="1777339362483" TEXT="verwendet einen Allocator">
|
||||
<arrowlink COLOR="#47c9c9" DESTINATION="ID_1644044904" ENDARROW="Default" ENDINCLINATION="6;-56;" ID="Arrow_ID_1619156849" STARTARROW="None" STARTINCLINATION="-20;33;"/>
|
||||
<node CREATED="1777328509534" ID="ID_1944871660" MODIFIED="1777328772390" TEXT="sollte mich an mein Allokatoren-Konzept-Framework halten....">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
auch wenn es erst mal gedanklich schwerfällt, und für die zunächst angestrebte <i>naive Implementierung</i> wie ein sinnloser Tanz aussieht; denn dieses Framework ist gut durchdacht und sollte langsam zu einem Rückgrat für Allokationsvorgänge werden. Zur Erinnerung: dieses Konzept habe ich an die Stelle eines »globalen Allokators« gestellt. Zunächst hatten wir ja den Ansatz mit Christian's "Mempool" — und ich habe mich jahrelang mit der Planlosigkeit dieser Idee herumgeplagt, bis ich langsam verstanden habe, was daran schief ist....
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<arrowlink COLOR="#345274" DESTINATION="ID_1644685634" ENDARROW="Default" ENDINCLINATION="-529;1146;" ID="Arrow_ID_491300253" STARTARROW="None" STARTINCLINATION="-2513;119;"/>
|
||||
<node CREATED="1777328785465" ID="ID_1420803190" MODIFIED="1777328795899" TEXT="wir verwenden einen unterliegenden »Allokator«"/>
|
||||
<node CREATED="1777329110029" ID="ID_1388976086" MODIFIED="1777329129190" TEXT="Hier ist der base_type nur eine Speicherkachel"/>
|
||||
<node CREATED="1777328936437" ID="ID_558800878" MODIFIED="1777328948520">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Allokatoren sind <i>composable</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<node CREATED="1777329176181" ID="ID_381595151" MODIFIED="1777329187583" TEXT="der unterliegende Allokator mag eine Pool-Hierarchie haben"/>
|
||||
<node CREATED="1777329188341" ID="ID_323248358" MODIFIED="1777329208588" TEXT="der darüberliegende Allokator hält die Ownership und leiht Allokationen aus"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1777329356340" ID="ID_235497917" MODIFIED="1777330790158" TEXT="die »Ownership«-Ebene muß zunächst ausgeklammert bleiben">
|
||||
<arrowlink COLOR="#ce0827" DESTINATION="ID_860687505" ENDARROW="Default" ENDINCLINATION="135;-10;" ID="Arrow_ID_1201137989" STARTARROW="None" STARTINCLINATION="-14;74;"/>
|
||||
<node CREATED="1777329370541" ID="ID_1130144304" MODIFIED="1777330545220">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
weil sie für einen <i>naiven Allokator</i> nur schlecht zu implementieren ist
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...da wir ja letztlich alles an den globalen Allokator durchreichen wollen (weil der's sowiso besser kann), haben wir keinerlei Struktur mehr, die wir uns zunutze machen können ⟹ für jede de-Allokation müssen wir den Tracking-Eintrag suchen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1777329418716" ID="ID_1818082361" MODIFIED="1777330552447">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
für eine <i>Pool-Hierarchie</i> dagegen ist eine effiziente Implementierung möglich
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
hierbei kann man sich die bekannte Element-Größe zunutze machen
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
zunächst findet man den zuständigen Pool
|
||||
</li>
|
||||
<li>
|
||||
sodann braucht es ggfs. einen Meta-Index oder eine sonstige Hilfstruktur, um ggfs. den jeweilgen Extent zu finden
|
||||
</li>
|
||||
<li>
|
||||
im Extent muß man dann ein Bit in der Belegungs-Bitmap flippen
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819169143" ID="ID_1367745941" MODIFIED="1777050391595" TEXT="Schnittstelle für asynchrones Messaging bedenken">
|
||||
<arrowlink COLOR="#fefab1" DESTINATION="ID_1503539674" ENDARROW="Default" ENDINCLINATION="-233;10;" ID="Arrow_ID_451160831" STARTARROW="None" STARTINCLINATION="92;5;"/>
|
||||
|
|
@ -146102,20 +146285,66 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819181261" ID="ID_1026628741" MODIFIED="1776819956356" TEXT="alternative synchrone Schnittstelle bereitstellen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1776819196456" ID="ID_1985019567" MODIFIED="1776819221279">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
zunächst wird <u>nur diese</u> genutzt
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1777324291441" ID="ID_336948545" MODIFIED="1777324316333" TEXT="ist relevant für »sofort«-Allokation (Fallback)"/>
|
||||
<node CREATED="1776819226503" ID="ID_1059062619" MODIFIED="1777051865655" TEXT="sollte kongruent zum Messaging sein"/>
|
||||
<node CREATED="1776819244365" ID="ID_933815031" MODIFIED="1776819258406" TEXT="aber stets ein »pull« vom lokalen Manager"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1777324327301" ID="ID_1965528564" MODIFIED="1777324340058" TEXT="lege zunächst einen »naiven« Memory-Manager an">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1777324341484" ID="ID_269815037" MODIFIED="1777324543018" TEXT="einzig sinnvoll: direkt vom/zum Heap">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Es ist nicht sinnvoll, eine »vorläufige« Implementierung eines heterogenen Memory-Managers zu machen. Da ist nämlich der Heap-Allokator<i>  stets besser geeignet</i> — ist inzwischen einigermaßen schwer geworden, den Heap-Allokator überhaupt noch zu schlagen. Mithin wird das auch gleich eine gute <b>Baseline-Implementierung</b> für Performance-Messungen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1777324354872" ID="ID_33339353" MODIFIED="1777324362776" TEXT="später geplant: System von Pools">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node CREATED="1777324463807" ID="ID_64824853" MODIFIED="1777324471021" TEXT="dabei ist aber sehr vieles noch unklar"/>
|
||||
<node CREATED="1777324471804" ID="ID_409561662" MODIFIED="1777324485502" TEXT="kann das überhaupt funktionieren, ohne massiv Speicher zu verschwenden?"/>
|
||||
<node CREATED="1777324494396" ID="ID_23718162" MODIFIED="1777324511253" TEXT="möglicherweise ist eine dynamische Steuerung notwendig + fall-Back auf den Heap-Allokator"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1777324560690" ID="ID_318881353" MODIFIED="1777324567396" TEXT="Rahmen per Test abstecken">
|
||||
<node COLOR="#338800" CREATED="1777339205760" ID="ID_1644044904" MODIFIED="1777339355233" TEXT="demonstrate_AllocatorInterface">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das ist nun wirklich mehr eine Demonstration, denn ich kann da praktisch nichts verifizieren (außer daß es keinen SEGFAULT gibt, wenn ich in den allozierten Speicher schreibe). Wenigstens ist damit schon mal sichergestellt, daß der Code durch den Compiler geht
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<linktarget COLOR="#47c9c9" DESTINATION="ID_1644044904" ENDARROW="Default" ENDINCLINATION="6;-56;" ID="Arrow_ID_1619156849" SOURCE="ID_1470622255" STARTARROW="None" STARTINCLINATION="-20;33;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1777330585249" ID="ID_1415692101" MODIFIED="1777330598348" TEXT="Ergebnis / Einsichten">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1777330604883" ID="ID_860687505" MODIFIED="1777330785076" TEXT="»Ownership« ist zunächst freischwebend">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das heißt, wir müssen uns darauf verlassen, daß der ganze komplexe Klapperatismus absolut korrekt funktioniert; wenn dem so ist, genügt es, einfach blindlings neue Allokationen an die lokalen Pools zu schicken und freizugeben was aus den lokalen Pools zurückgesendet wird. Das <b>hat zur Konsequenz</b> daß gewisse zusätzliche Konsistenzchecks eingebaut werden müssen, damit sich die Korrektheit auch erweisen läßt
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<linktarget COLOR="#ce0827" DESTINATION="ID_860687505" ENDARROW="Default" ENDINCLINATION="135;-10;" ID="Arrow_ID_1201137989" SOURCE="ID_235497917" STARTARROW="None" STARTINCLINATION="-14;74;"/>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue