BufferProvider: testing of heuristics...
This commit is contained in:
parent
5fafb16807
commit
077ef891ca
3 changed files with 115 additions and 23 deletions
|
|
@ -57,14 +57,16 @@
|
||||||
#include "vault/mem/buffhandle.hpp" //////////////TODO
|
#include "vault/mem/buffhandle.hpp" //////////////TODO
|
||||||
//#include "lib/local-slice.hpp"
|
//#include "lib/local-slice.hpp"
|
||||||
//#include "lib/depend.hpp"
|
//#include "lib/depend.hpp"
|
||||||
|
#include "lib/iter-explorer.hpp"
|
||||||
|
#include "lib/format-string.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
#include "lib/nocopy.hpp"
|
#include "lib/nocopy.hpp"
|
||||||
#include "lib/meta/trait.hpp"//////////////TODO
|
#include "lib/meta/trait.hpp"//////////////TODO
|
||||||
|
|
||||||
#include <boost/lockfree/queue.hpp>
|
#include <boost/lockfree/queue.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
//#include <tuple>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,7 +79,7 @@ namespace mem {
|
||||||
const size_t INQUEUE_SIZ = 30; ///< initial size of the lock-free provision queue
|
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 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 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/
|
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 (not block.used)
|
||||||
{
|
{
|
||||||
if (sizRequest > block.alloc.siz)
|
if (sizRequest > block.alloc.siz)
|
||||||
block.score -= UNUSABLE_MALUS;
|
block.score -= MISFIT_PENALTY;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double wasted = block.alloc.siz - sizRequest;
|
double wasted = block.alloc.siz - sizRequest;
|
||||||
|
|
@ -387,7 +389,7 @@ namespace mem {
|
||||||
if (match)
|
if (match)
|
||||||
{
|
{
|
||||||
ENSURE (match->alloc.siz >= sizRequest);
|
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
|
if (match->score > maxScore_) // reduce score by waste factor as malus
|
||||||
maxScore_ = match->score;
|
maxScore_ = match->score;
|
||||||
}
|
}
|
||||||
|
|
@ -436,6 +438,14 @@ namespace mem {
|
||||||
return std::ranges::count_if (memPool_.blocks_
|
return std::ranges::count_if (memPool_.blocks_
|
||||||
,[](Block const& b){ return not b.used; });
|
,[](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();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,15 @@ namespace test {
|
||||||
const size_t SIZ10{10};
|
const size_t SIZ10{10};
|
||||||
const size_t SIZ20{20};
|
const size_t SIZ20{20};
|
||||||
const size_t SIZ30{30};
|
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
|
/** @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
|
void
|
||||||
verify_matchAlloc()
|
verify_matchAlloc()
|
||||||
|
|
@ -109,6 +121,7 @@ namespace test {
|
||||||
LocalMemPool pool;
|
LocalMemPool pool;
|
||||||
CHECK (watch(pool).isEmpty());
|
CHECK (watch(pool).isEmpty());
|
||||||
|
|
||||||
|
// Feed new allocations through the in-queue...
|
||||||
pool.add (MEM1, SIZ10);
|
pool.add (MEM1, SIZ10);
|
||||||
CHECK (watch(pool).cnt(SIZ10) == 0); // new allotment not ingested yet,....
|
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
|
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).cnt(SIZ30) == 1);
|
||||||
CHECK (watch(pool).cntFree() == 3);
|
CHECK (watch(pool).cntFree() == 3);
|
||||||
|
|
||||||
auto [m1,s1] = pool.retrieve (SIZ20 -2);
|
// Request some allocations from the pool....
|
||||||
CHECK (SIZ20 == s1);
|
auto [m1,s1] = pool.retrieve (SIZ10 -2);
|
||||||
CHECK (MEM2 == m1);
|
CHECK (SIZ10 == s1);
|
||||||
|
CHECK (MEM1 == m1);
|
||||||
CHECK (watch(pool).cntFree() == 2);
|
CHECK (watch(pool).cntFree() == 2);
|
||||||
auto [m2,s2] = pool.retrieve (SIZ10);
|
auto [m2,s2] = pool.retrieve (SIZ20);
|
||||||
CHECK (SIZ10 == s2);
|
CHECK (SIZ20 == s2);
|
||||||
CHECK (MEM1 == m2);
|
CHECK (MEM2 == m2);
|
||||||
CHECK (watch(pool).size() == 3);
|
CHECK (watch(pool).size() == 3);
|
||||||
CHECK (watch(pool).cntFree() == 1);
|
CHECK (watch(pool).cntFree() == 1);
|
||||||
CHECK (pool.canServe(SIZ10));
|
CHECK (pool.canServe(SIZ10)); // only the SIZ30-allocation is available from the pool
|
||||||
CHECK (pool.canServe(SIZ20));
|
CHECK (pool.canServe(SIZ20)); // which however could serve also smaller requests...
|
||||||
CHECK (pool.canServe(SIZ30));
|
CHECK (pool.canServe(SIZ30));
|
||||||
|
|
||||||
|
// Return allocations back into the pool
|
||||||
pool.reAdd (m2);
|
pool.reAdd (m2);
|
||||||
pool.reAdd (m1);
|
pool.reAdd (m1);
|
||||||
CHECK (watch(pool).cntFree() == 3);
|
CHECK (watch(pool).cntFree() == 3);
|
||||||
|
|
||||||
|
// Shrink / clean-up will return allocations through a consumer
|
||||||
lib::IterQueue<size_t> returned;
|
lib::IterQueue<size_t> returned;
|
||||||
uint cnt = pool.cleanup(0.5
|
auto returnAlloc = [&](auto, size_t siz){ returned.feed (siz); };
|
||||||
,[&](auto, size_t siz){ returned.feed (siz);});
|
|
||||||
|
uint cnt = pool.cleanup(0.5 ,returnAlloc); // heuristic partial clean-up
|
||||||
CHECK (cnt == 1);
|
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).cntFree() == 2);
|
||||||
CHECK (watch(pool).size() == 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
|
void
|
||||||
verify_selectAlloc()
|
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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146072,14 +146072,23 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777050457640" ID="ID_55967690" MODIFIED="1777084249904" TEXT="Dokumentieren per Test">
|
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777050457640" ID="ID_55967690" MODIFIED="1777084249904" TEXT="Dokumentieren per Test">
|
||||||
<icon BUILTIN="pencil"/>
|
<icon BUILTIN="pencil"/>
|
||||||
<node CREATED="1777050471715" ID="ID_694034532" MODIFIED="1777050476406" TEXT="LocalMemPool_test"/>
|
<node COLOR="#435e98" CREATED="1777050471715" ID="ID_694034532" MODIFIED="1777250236591" STYLE="fork" TEXT="LocalMemPool_test">
|
||||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777052679889" ID="ID_1809859332" MODIFIED="1777084246555" TEXT="simpleUse()">
|
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||||
<icon BUILTIN="pencil"/>
|
<icon BUILTIN="info"/>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1777052647931" ID="ID_1721020388" MODIFIED="1777084246554" TEXT="verify_matchAlloc">
|
<node COLOR="#338800" CREATED="1777052679889" ID="ID_1809859332" MODIFIED="1777250222071" TEXT="simpleUse()">
|
||||||
<icon BUILTIN="pencil"/>
|
<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ß dieser nicht fü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>
|
||||||
<node CREATED="1777052660303" ID="ID_1192169892" MODIFIED="1777052665482" TEXT="verify_selectAlloc"/>
|
|
||||||
<node CREATED="1777052670304" ID="ID_924547131" MODIFIED="1777052674241" TEXT="verify_pruneAlloc"/>
|
<node CREATED="1777052670304" ID="ID_924547131" MODIFIED="1777052674241" TEXT="verify_pruneAlloc"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue