BufferProvider: testing of heuristics...

This commit is contained in:
Fischlurch 2026-04-27 01:52:21 +02:00
parent 5fafb16807
commit 077ef891ca
3 changed files with 115 additions and 23 deletions

View file

@ -57,14 +57,16 @@
#include "vault/mem/buffhandle.hpp" //////////////TODO
//#include "lib/local-slice.hpp"
//#include "lib/depend.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/format-string.hpp"
#include "lib/util.hpp"
#include "lib/nocopy.hpp"
#include "lib/meta/trait.hpp"//////////////TODO
#include <boost/lockfree/queue.hpp>
#include <algorithm>
//#include <tuple>
#include <utility>
#include <string>
#include <list>
@ -77,7 +79,7 @@ namespace mem {
const size_t INQUEUE_SIZ = 30; ///< initial size of the lock-free provision queue
const uint MATCH_SCORE = 10; ///< score to add when a buffer can be used to satisfy a request
const uint UNUSABLE_MALUS = 2; ///< reduce score whenever a buffer is too small to be useful
const uint MISFIT_PENALTY = 2; ///< reduce score whenever a buffer is too small to be useful
const double USAGE_WEIGHT = 0.9; ///< degree to which very frequent usage counteracts waste of memory
const double CLOSE_MATCH = 0.2; ///< fraction of wasted memory that still counts as /good match/
@ -370,7 +372,7 @@ namespace mem {
if (not block.used)
{
if (sizRequest > block.alloc.siz)
block.score -= UNUSABLE_MALUS;
block.score -= MISFIT_PENALTY;
else
{
double wasted = block.alloc.siz - sizRequest;
@ -387,7 +389,7 @@ namespace mem {
if (match)
{
ENSURE (match->alloc.siz >= sizRequest);
match->score += int(MATCH_SCORE * (1 - (1 - double(sizRequest) / match->alloc.siz)));
match->score += int(MATCH_SCORE * double(sizRequest) / match->alloc.siz);
if (match->score > maxScore_) // reduce score by waste factor as malus
maxScore_ = match->score;
}
@ -436,6 +438,14 @@ namespace mem {
return std::ranges::count_if (memPool_.blocks_
,[](Block const& b){ return not b.used; });
}
auto
allScores()
{
return lib::explore (memPool_.blocks_)
.transform([](Block const& b){ return std::string{util::_Fmt{"%d:%02d"} % b.alloc.siz % b.score}; })
.effuse();
}
};

View file

@ -55,7 +55,15 @@ namespace test {
const size_t SIZ10{10};
const size_t SIZ20{20};
const size_t SIZ30{30};
}
Buff* const MEM11 = fake_Buffer(11);
Buff* const MEM12 = fake_Buffer(12);
Buff* const MEM13 = fake_Buffer(13);
Buff* const MEM21 = fake_Buffer(21);
Buff* const MEM31 = fake_Buffer(31);
Buff* const MEM32 = fake_Buffer(32);
Buff* const MEM33 = fake_Buffer(33);
}
@ -102,6 +110,10 @@ namespace test {
/** @test allocations are accounted by size
* - new memory is posted through an in-queue
* - for each request, the closest matching allocation is handed out
* - a partial clean-up removes unused / bad matching allocations first
* - a complete clean-up clears out the pool
*/
void
verify_matchAlloc()
@ -109,6 +121,7 @@ namespace test {
LocalMemPool pool;
CHECK (watch(pool).isEmpty());
// Feed new allocations through the in-queue...
pool.add (MEM1, SIZ10);
CHECK (watch(pool).cnt(SIZ10) == 0); // new allotment not ingested yet,....
CHECK (not watch(pool).isEmpty()); // however, there is an entry in the in-queue
@ -127,39 +140,99 @@ namespace test {
CHECK (watch(pool).cnt(SIZ30) == 1);
CHECK (watch(pool).cntFree() == 3);
auto [m1,s1] = pool.retrieve (SIZ20 -2);
CHECK (SIZ20 == s1);
CHECK (MEM2 == m1);
// Request some allocations from the pool....
auto [m1,s1] = pool.retrieve (SIZ10 -2);
CHECK (SIZ10 == s1);
CHECK (MEM1 == m1);
CHECK (watch(pool).cntFree() == 2);
auto [m2,s2] = pool.retrieve (SIZ10);
CHECK (SIZ10 == s2);
CHECK (MEM1 == m2);
auto [m2,s2] = pool.retrieve (SIZ20);
CHECK (SIZ20 == s2);
CHECK (MEM2 == m2);
CHECK (watch(pool).size() == 3);
CHECK (watch(pool).cntFree() == 1);
CHECK (pool.canServe(SIZ10));
CHECK (pool.canServe(SIZ20));
CHECK (pool.canServe(SIZ10)); // only the SIZ30-allocation is available from the pool
CHECK (pool.canServe(SIZ20)); // which however could serve also smaller requests...
CHECK (pool.canServe(SIZ30));
// Return allocations back into the pool
pool.reAdd (m2);
pool.reAdd (m1);
CHECK (watch(pool).cntFree() == 3);
// Shrink / clean-up will return allocations through a consumer
lib::IterQueue<size_t> returned;
uint cnt = pool.cleanup(0.5
,[&](auto, size_t siz){ returned.feed (siz);});
auto returnAlloc = [&](auto, size_t siz){ returned.feed (siz); };
uint cnt = pool.cleanup(0.5 ,returnAlloc); // heuristic partial clean-up
CHECK (cnt == 1);
CHECK (join(returned) == "30"_expect);
CHECK (join(returned) == "30"_expect); // the SIZ30 allocation was not used yet, and thus returned first
CHECK (watch(pool).cntFree() == 2);
CHECK (watch(pool).size() == 2);
cnt = 0;
cnt = pool.yield(2, SIZ20, returnAlloc); // explicit request to return two SIZ20 allocations...
CHECK (cnt == 1); // yet there is only one such block in the pool
CHECK (join(returned) == "30, 20"_expect);
CHECK (watch(pool).cnt(SIZ10) == 1);
CHECK (watch(pool).cnt(SIZ20) == 0);
CHECK (watch(pool).cnt(SIZ30) == 0);
CHECK (watch(pool).cntFree() == 1);
CHECK (watch(pool).size() == 1);
// add a further allocation with SIZ30
CHECK (not pool.canServe(SIZ30));
pool.add (MEM3, SIZ30);
CHECK (watch(pool).size() == 1);
CHECK ( pool.canServe(SIZ30));
CHECK (watch(pool).size() == 2);
cnt = 0;
cnt = pool.cleanup(1.0 ,returnAlloc); // complete clean-up requested
CHECK (cnt == 2);
CHECK (join(returned) == "30, 20, 30, 10"_expect);
CHECK (watch(pool).isEmpty());
}
/** @test
/** @test the pool picks allocations heuristically
*/
void
verify_selectAlloc()
{
LocalMemPool pool;
pool.add (MEM11, SIZ10);
pool.add (MEM31, SIZ30);
pool.add (MEM32, SIZ30);
pool.add (MEM33, SIZ30);
uint cnt = pool.reserve(4, SIZ10); // request to reserve 4 blocks of SIZ10
CHECK (cnt == 1); // yet only one block is a good match and thus reserved
cnt = pool.reserve(2, SIZ30-5);
CHECK (cnt == 2); // these two reservation requests can be satisfied
cnt = pool.reserve(2, SIZ10);
CHECK (cnt == 0); // but the pool is now unable to reserve more SIZ10 (without wasting memory)
auto [m1,s1] = pool.retrieve (SIZ10); // actual allocation requests are always served however, if possible at all
auto [m2,s2] = pool.retrieve (SIZ10);
auto [m3,s3] = pool.retrieve (SIZ10);
auto [m4,s4] = pool.retrieve (SIZ20+SIZ20); // this one can certainly not be satisfied
CHECK (MEM11 == m1); // the first request gets the good match
CHECK (MEM33 == m2); // the following ones are satisfied with an over-allocation
CHECK (MEM32 == m3);
CHECK (not m4);
CHECK (watch(pool).cntFree() == 1);
CHECK (watch(pool).size() == 4);
// return all blocks...
pool.reAdd (m1);
pool.reAdd (m2);
pool.reAdd (m3);
VERIFY_FAIL ("unknown allocation", pool.reAdd (m4))
VERIFY_FAIL ("unknown allocation", pool.reAdd (fake_Buffer(12345)))
VERIFY_FAIL ("not marked as used", pool.reAdd (MEM31))
CHECK (watch(pool).cntFree() == 4);
SHOW_EXPR(join(watch(pool).allScores()))
}

View file

@ -146072,14 +146072,23 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777050457640" ID="ID_55967690" MODIFIED="1777084249904" TEXT="Dokumentieren per Test">
<icon BUILTIN="pencil"/>
<node CREATED="1777050471715" ID="ID_694034532" MODIFIED="1777050476406" TEXT="LocalMemPool_test"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777052679889" ID="ID_1809859332" MODIFIED="1777084246555" TEXT="simpleUse()">
<icon BUILTIN="pencil"/>
<node COLOR="#435e98" CREATED="1777050471715" ID="ID_694034532" MODIFIED="1777250236591" STYLE="fork" TEXT="LocalMemPool_test">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777052647931" ID="ID_1721020388" MODIFIED="1777084246554" TEXT="verify_matchAlloc">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1777052679889" ID="ID_1809859332" MODIFIED="1777250222071" TEXT="simpleUse()">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1777052647931" ID="ID_1721020388" MODIFIED="1777252434731" TEXT="verify_matchAlloc">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777052660303" ID="ID_1192169892" MODIFIED="1777255538360" TEXT="verify_selectAlloc">
<icon BUILTIN="pencil"/>
<node CREATED="1777255542851" ID="ID_1123855450" MODIFIED="1777255577205" TEXT="hier gezielt einen Mismatch provozieren"/>
<node CREATED="1777255578550" ID="ID_912741988" MODIFIED="1777255590015" TEXT="zeigen da&#xdf; dieser nicht f&#xfc;r Reservierung herangezogen wird..."/>
<node CREATED="1777255590787" ID="ID_1143192408" MODIFIED="1777255607747" TEXT="jedoch stets versucht wird, alle Allokations-Requests direkt zu befriedigen"/>
<node CREATED="1777255608745" ID="ID_832626969" MODIFIED="1777255619923" TEXT="dann aber auch die Scores anschauen..."/>
</node>
<node CREATED="1777052660303" ID="ID_1192169892" MODIFIED="1777052665482" TEXT="verify_selectAlloc"/>
<node CREATED="1777052670304" ID="ID_924547131" MODIFIED="1777052674241" TEXT="verify_pruneAlloc"/>
</node>
</node>