1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-08 03:27:04 +09:00
This commit is contained in:
vsadov 2024-08-28 16:33:22 -07:00
parent 4235dff281
commit 4f409d1d40
2 changed files with 77 additions and 60 deletions

View file

@ -143,8 +143,8 @@ void SatoriRecycler::Initialize(SatoriHeap* heap)
m_gen1CountAtLastGen2 = 0;
m_gen1Budget = MIN_GEN1_BUDGET;
m_totalBudget = MIN_GEN1_BUDGET;
m_totalLimit = m_totalBudget;
m_gen2Limit = MIN_GEN1_BUDGET;
m_nextGcIsFullGc = false;
m_prevCondemnedGeneration = 2;
m_gcNextTimeTarget = 0;
@ -430,6 +430,9 @@ size_t SatoriRecycler::IncrementGen0Count()
void SatoriRecycler::TryStartGC(int generation, gc_reason reason)
{
if (m_nextGcIsFullGc)
generation = 2;
int newState = SatoriUtil::IsConcurrent() ? GC_STATE_CONCURRENT : GC_STATE_BLOCKING;
if (m_gcState == GC_STATE_NONE &&
Interlocked::CompareExchange(&m_gcState, newState, GC_STATE_NONE) == GC_STATE_NONE)
@ -865,22 +868,14 @@ void SatoriRecycler::MaybeTriggerGC(gc_reason reason)
return;
}
// if allocated enough, we can tax the app - in terms of doing a GC
if (m_gen1AddedSinceLastCollection > m_gen1Budget)
{
generation = 1;
}
size_t totalAddedEstimate = m_gen2AddedSinceLastCollection +
m_gen1AddedSinceLastCollection;
if (totalAddedEstimate > m_totalBudget)
{
generation = 2;
}
// just make sure gen2 happens eventually.
if (generation > 0 &&
m_gcCount[1] - m_gen1CountAtLastGen2 > 16)
// if allocated enough into gen2 (huge objects case), trigger gen2 GC
if (m_gen2AddedSinceLastCollection > (m_gen2Limit / GEN2_SURV_TARGET * (GEN2_SURV_TARGET - 1)))
{
generation = 2;
}
@ -910,70 +905,86 @@ void SatoriRecycler::AdjustHeuristics()
if (m_prevCondemnedGeneration == 2)
{
m_totalLimit = occupancy * GEN2_SURV_TARGET;
m_gen2Limit = tenuredOccupancy * GEN2_SURV_TARGET;
}
// what we may have after collection
size_t currentTotalEstimate;
if (m_condemnedGeneration == 2)
{
currentTotalEstimate =
(occupancy + m_gen2AddedSinceLastCollection + m_gen1AddedSinceLastCollection) / GEN2_SURV_TARGET;
}
else
{
currentTotalEstimate = tenuredOccupancy + m_gen2AddedSinceLastCollection +
(ephemeralOccupancy + m_gen1AddedSinceLastCollection) / EPH_SURV_TARGET;
}
//// estimate how much of ephemeral set will survive current collection.
//// we will size our next GC budget relative to that size.
//ephemeralOccupancy = (ephemeralOccupancy + m_gen1AddedSinceLastCollection) / EPH_SURV_TARGET;
m_totalBudget = m_totalLimit > currentTotalEstimate ?
max(MIN_GEN1_BUDGET, m_totalLimit - currentTotalEstimate) :
MIN_GEN1_BUDGET;
// we will try not to use the last 10%
size_t available = GetAvailableMemory() * 9 / 10;
m_totalBudget = min(m_totalBudget, available);
if (ephemeralOccupancy == 0)
{
// after mass-promoting GC ephemeral size will be 0, so temporarily assume it is 1/8 of total
ephemeralOccupancy = occupancy / 8;
}
// we look for 1 / EPH_SURV_TARGET ephemeral survivorship, thus budget is ephemeralOccupancy * EPH_SURV_TARGET
size_t newGen1Budget = max(MIN_GEN1_BUDGET, ephemeralOccupancy * EPH_SURV_TARGET);
// we look for 1 / EPH_SURV_TARGET ephemeral survivorship, the diff is roughly our budget
size_t newGen1Budget = max(MIN_GEN1_BUDGET, ephemeralOccupancy * (EPH_SURV_TARGET - 1));
// smooth the budget a bit
// TUNING: using exponential smoothing with alpha == 1/2. is it a good smooth/lag balance?
m_gen1Budget = (m_gen1Budget + newGen1Budget) / 2;
newGen1Budget = (m_gen1Budget + newGen1Budget) / 2;
//// we will try not to use the last 10%
//size_t available = GetAvailableMemory() * 9 / 10;
//newGen1Budget = min(newGen1Budget, available);
//size_t oldGen2 = m_gen2Limit / GEN2_SURV_TARGET;
//size_t gen2TotalBudget = m_gen2Limit - oldGen2;
//if (m_gen2AddedSinceLastCollection < gen2TotalBudget)
//{
// size_t gen2RemainingBudget = gen2TotalBudget - m_gen2AddedSinceLastCollection;
// gen2RemainingBudget = min(gen2RemainingBudget, available - newGen1Budget);
// m_gen2Limit = min(m_gen2Limit, oldGen2 + m_gen2AddedSinceLastCollection + gen2RemainingBudget);
//}
m_gen1Budget = newGen1Budget;
// now figure if we will promote
// now figure if the next GC will be a Full GC
m_nextGcIsFullGc = false;
if (m_condemnedGeneration != 2)
{
// we do full GC every 16 GCs
if (m_gcCount[1] - m_gen1CountAtLastGen2 > 16)
{
m_nextGcIsFullGc = true;
}
// also do full GC if gen2 exceeded the limit
if (tenuredOccupancy > m_gen2Limit)
{
m_nextGcIsFullGc = true;
}
}
// now figure if we will promote in the current GC
m_promoteAllRegions = false;
size_t promotionEstimate = m_promotionEstimate;
m_promotionEstimate = 0;
if (m_condemnedGeneration == 2)
{
// we promote all in gen2 collections. This is the simplest option.
// - gen2 objects will be collected, so would need to ensure cards are only for live ones.
// - compacting will need to deal with mixed regions - which generation? what happens to cards?
// - there is inefficiency in promoting some gen1 survivors early (1 mark vs. nAge marks), but:
// - still needs to survive at least 1 mark
// - not a big deal if gen2 >> gen1 and it is good that real gen2 stay in place.
// - for gen2 << gen1 this will increase gen2 float by about ~gen1 surv size
// nepotism may lead to more gen2 collections
// but generational behavior is already not working well for gen1 >> gen2
//
// Another idea is for gen1 >> gen2 case make everything gen1 and reset cards.
// the differences:
// will need to rejuvenate handles and syncblocks
// true gen2 will participate in gen1 for nAging collections, then promote back (and cause gen2?)
// how do we know the size of gen2?
m_promoteAllRegions = true;
m_gen1CountAtLastGen2 = (int)m_gcCount[1];
}
else
{
if (promotionEstimate > Gen1RegionCount() / 2)
// if (promotionEstimate > Gen1RegionCount() / 2)
{
// will promote too many, just promote all
m_promoteAllRegions = true;
}
if (m_gen1Budget <= m_totalBudget)
{
// TODO: VS next GC will be gen2, just promote all.
// m_promoteAllRegions = true;
// TODO: and more importantly alloc should skip gen1.
// no point in setting cards.
}
}
}
@ -1156,11 +1167,15 @@ void SatoriRecycler::BlockingCollectImpl()
(uint32_t)0,
(uint32_t)0);
m_gen1AddedSinceLastCollection = 0;
if (m_condemnedGeneration == 2)
{
m_gen2AddedSinceLastCollection = 0;
}
m_prevCondemnedGeneration = m_condemnedGeneration;
m_condemnedGeneration = 0;
m_gcState = GC_STATE_NONE;
m_gen1AddedSinceLastCollection = 0;
m_gen2AddedSinceLastCollection = 0;
if (SatoriUtil::IsConcurrent() && !m_deferredSweepRegions->IsEmpty())
{
@ -3179,7 +3194,8 @@ void SatoriRecycler::PromoteSurvivedHandlesAndFreeRelocatedRegionsWorker()
[&](int p)
{
sc.thread_number = p;
GCScan::GcPromotionsGranted(m_condemnedGeneration, 2, &sc);
// age gen1 handles
GCScan::GcPromotionsGranted(1, 2, &sc);
}
);
}
@ -3299,7 +3315,7 @@ void SatoriRecycler::DenyRelocation()
m_isRelocating = false;
// put all affected regions into staying queue
if (m_promoteAllRegions)
if (m_condemnedGeneration == 2 || m_promoteAllRegions)
{
m_occupancyAcc[2] = 0;
m_relocatableTenuredEstimate = 0;

View file

@ -192,8 +192,9 @@ private:
int64_t m_gcDurationMillis[3];
size_t m_gen1Budget;
size_t m_totalBudget;
size_t m_totalLimit;
size_t m_gen2Limit;
size_t m_nextGcIsFullGc;
size_t m_condemnedRegionsCount;
size_t m_deferredSweepCount;
size_t m_gen1AddedSinceLastCollection;