Skip to content

Commit 88bd42d

Browse files
author
William Kemper
committed
8314599: [GenShen] Couple adaptive tenuring and generation size budgeting
Reviewed-by: kdnilsen, xpeng, ruili
1 parent 322f3a3 commit 88bd42d

5 files changed

Lines changed: 93 additions & 68 deletions

File tree

src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp

Lines changed: 58 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@
3838

3939
using idx_t = ShenandoahSimpleBitMap::idx_t;
4040

41-
typedef struct {
42-
ShenandoahHeapRegion* _region;
43-
size_t _live_data;
44-
} AgedRegionData;
45-
4641
static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) {
4742
if (a._live_data < b._live_data)
4843
return -1;
@@ -321,21 +316,57 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c
321316
immediate_garbage);
322317
}
323318

324-
// Select for inclusion into the collection set all regions whose age is at or above tenure age and for which the
325-
// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We
326-
// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true.
327-
// All entries are initialized to false before calling this function.
319+
void ShenandoahGenerationalHeuristics::add_tenured_regions_to_collection_set(const size_t old_promotion_reserve,
320+
ShenandoahGenerationalHeap *const heap,
321+
size_t candidates, AgedRegionData* sorted_regions) {
322+
size_t old_consumed = 0;
323+
if (candidates > 0) {
324+
// Sort in increasing order according to live data bytes. Note that
325+
// candidates represents the number of regions that qualify to be promoted
326+
// by evacuation.
327+
QuickSort::sort<AgedRegionData>(sorted_regions, candidates,
328+
compare_by_aged_live);
329+
330+
size_t selected_regions = 0;
331+
size_t selected_live = 0;
332+
for (size_t i = 0; i < candidates; i++) {
333+
ShenandoahHeapRegion *const region = sorted_regions[i]._region;
334+
const size_t region_live_data = sorted_regions[i]._live_data;
335+
const size_t promotion_need = (size_t)(region_live_data * ShenandoahPromoEvacWaste);
336+
if (old_consumed + promotion_need > old_promotion_reserve) {
337+
// We rejected the remaining promotable regions from the collection set
338+
// because we have no room to hold their evacuees. We do not need to
339+
// iterate the remaining regions to estimate the amount we expect to
340+
// promote because we know it directly form the census we computed
341+
// during the preceding mark phase.
342+
break;
343+
}
344+
345+
old_consumed += promotion_need;
346+
heap->collection_set()->add_region(region);
347+
selected_regions++;
348+
selected_live += region_live_data;
349+
}
350+
log_debug(gc, ergo)( "Preselected %zu regions containing " PROPERFMT " live data,"
351+
" consuming: " PROPERFMT " of budgeted: " PROPERFMT,
352+
selected_regions, PROPERFMTARGS(selected_live),
353+
PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve));
354+
}
355+
}
356+
357+
// Select for inclusion into the collection set all regions whose age is at or
358+
// above tenure age and for which the
359+
// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage).
328360
//
329-
// During the subsequent selection of the collection set, we give priority to these promotion set candidates.
330361
// Without this prioritization, we found that the aged regions tend to be ignored because they typically have
331362
// much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are
332363
// repeatedly excluded from the collection set, the amount of live memory within the young generation tends to
333364
// accumulate and this has the undesirable side effect of causing young-generation collections to require much more
334365
// CPU and wall-clock time.
335366
//
336367
// A second benefit of treating aged regions differently than other regions during collection set selection is
337-
// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation
338-
// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be
368+
// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation
369+
// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be
339370
// reserved in the young generation.
340371
size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions,
341372
const size_t old_promotion_reserve) {
@@ -345,7 +376,6 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr
345376

346377
auto const heap = ShenandoahGenerationalHeap::heap();
347378

348-
size_t promo_potential = 0;
349379
size_t candidates = 0;
350380

351381
// Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require
@@ -386,66 +416,28 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr
386416
sorted_regions[candidates]._live_data = r->get_live_data_bytes();
387417
candidates++;
388418
}
389-
} else {
390-
// We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold.
391-
// Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to
392-
// old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that
393-
// the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes
394-
// place during a subsequent GC pass because more garbage is found within the region between now and then. This
395-
// should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold
396-
// is to choose the youngest age that demonstrates no "significant" further loss of population since the previous
397-
// age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population
398-
// samples, whereas we expect to observe exponential population decay for ages younger than the tenure age.
399-
//
400-
// In the case that certain regions which were anticipated to be promoted in place need to be promoted by
401-
// evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of
402-
// these regions. The likely outcome is that these regions will not be selected for evacuation or promotion
403-
// in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause
404-
// us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle.
405-
if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) {
406-
if (r->garbage() >= in_place_promotions.old_garbage_threshold()) {
407-
promo_potential += r->get_live_data_bytes();
408-
}
409-
}
410419
}
411-
// Note that we keep going even if one region is excluded from selection.
412-
// Subsequent regions may be selected if they have smaller live data.
413420
}
414421

415422
in_place_promotions.complete_planning();
416423

417-
// Sort in increasing order according to live data bytes. Note that candidates represents the number of regions
418-
// that qualify to be promoted by evacuation.
419-
size_t old_consumed = 0;
420-
if (candidates > 0) {
421-
size_t selected_regions = 0;
422-
size_t selected_live = 0;
423-
QuickSort::sort<AgedRegionData>(sorted_regions, candidates, compare_by_aged_live);
424-
for (size_t i = 0; i < candidates; i++) {
425-
ShenandoahHeapRegion* const region = sorted_regions[i]._region;
426-
const size_t region_live_data = sorted_regions[i]._live_data;
427-
const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste);
428-
if (old_consumed + promotion_need <= old_promotion_reserve) {
429-
old_consumed += promotion_need;
430-
heap->collection_set()->add_region(region);
431-
selected_regions++;
432-
selected_live += region_live_data;
433-
} else {
434-
// We rejected this promotable region from the collection set because we had no room to hold its copy.
435-
// Add this region to promo potential for next GC.
436-
promo_potential += region_live_data;
437-
assert(!heap->collection_set()->is_in(region), "Region %zu shouldn't be in the collection set", region->index());
438-
}
439-
// We keep going even if one region is excluded from selection because we need to accumulate all eligible
440-
// regions that are not preselected into promo_potential
441-
}
442-
log_debug(gc, ergo)("Preselected %zu regions containing " PROPERFMT " live data,"
443-
" consuming: " PROPERFMT " of budgeted: " PROPERFMT,
444-
selected_regions, PROPERFMTARGS(selected_live), PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve));
445-
}
424+
add_tenured_regions_to_collection_set(old_promotion_reserve, heap, candidates, sorted_regions);
446425

447-
log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential));
426+
const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
427+
const size_t tenurable_this_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold);
428+
const size_t tenurable_next_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold - 1);
429+
assert(tenurable_next_cycle >= tenurable_this_cycle,
430+
"Tenurable next cycle (" PROPERFMT ") should include tenurable this cycle (" PROPERFMT ")",
431+
PROPERFMTARGS(tenurable_next_cycle), PROPERFMTARGS(tenurable_this_cycle));
432+
433+
const size_t max_promotions = tenurable_this_cycle * ShenandoahPromoEvacWaste;
434+
const size_t old_consumed = MIN2(max_promotions, old_promotion_reserve);
435+
436+
// Don't include the bytes we expect to promote in this cycle in the next cycle
437+
const size_t promo_potential = (tenurable_next_cycle - tenurable_this_cycle) * ShenandoahPromoEvacWaste;
448438
heap->old_generation()->set_promotion_potential(promo_potential);
439+
log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential));
440+
449441
return old_consumed;
450442
}
451443

src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class ShenandoahHeap;
3434
class ShenandoahCollectionSet;
3535
class RegionData;
3636

37+
typedef struct {
38+
ShenandoahHeapRegion* _region;
39+
size_t _live_data;
40+
} AgedRegionData;
41+
3742
/*
3843
* This class serves as the base class for heuristics used to trigger and
3944
* choose the collection sets for young and global collections. It leans
@@ -50,7 +55,7 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics {
5055

5156
void choose_collection_set(ShenandoahCollectionSet* collection_set) override;
5257

53-
virtual void post_initialize() override;
58+
void post_initialize() override;
5459

5560
private:
5661
// Compute evacuation budgets prior to choosing collection set.
@@ -73,6 +78,12 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics {
7378
// to false.
7479
size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve);
7580

81+
// Select regions for inclusion in the collection set that are tenured, but do
82+
// not hold enough live data to warrant promotion in place.
83+
void add_tenured_regions_to_collection_set(size_t old_promotion_reserve,
84+
ShenandoahGenerationalHeap *const heap,
85+
size_t candidates, AgedRegionData* sorted_regions);
86+
7687
// Filter and sort remaining regions before adding to collection set.
7788
void filter_regions(ShenandoahCollectionSet* collection_set);
7889

src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,15 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop) {
171171
NOT_PRODUCT(update_total();)
172172
}
173173

174+
size_t ShenandoahAgeCensus::get_tenurable_bytes(const uint tenuring_threshold) const {
175+
assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
176+
size_t total = 0;
177+
const AgeTable* pv = _global_age_tables[_epoch];
178+
for (uint i = tenuring_threshold; i < MAX_COHORTS; i++) {
179+
total += pv->sizes[i];
180+
}
181+
return total * HeapWordSize;
182+
}
174183

175184
// Reset the epoch for the global age tables,
176185
// clearing all history.

src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,12 @@ class ShenandoahAgeCensus: public CHeapObj<mtGC> {
216216
// allocated when the concurrent marking was in progress.
217217
void update_census(size_t age0_pop);
218218

219+
// Return the total size of the population at or above the given threshold for the current epoch
220+
size_t get_tenurable_bytes(uint tenuring_threshold) const;
221+
222+
// As above, but use the current tenuring threshold
223+
size_t get_tenurable_bytes() const { return get_tenurable_bytes(tenuring_threshold()); }
224+
219225
// Reset the epoch, clearing accumulated census history
220226
// Note: this isn't currently used, but reserved for planned
221227
// future usage.

test/hotspot/gtest/gc/shenandoah/test_shenandoahAgeCensus.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class ShenandoahAgeCensusTest : public ::testing::Test {
6363
total += _cohort_populations[i];
6464
}
6565
}
66-
return total;
66+
return total * HeapWordSize;
6767
}
6868

6969
void promote_all_tenurable(const size_t tenuring_threshold) {
@@ -87,6 +87,13 @@ TEST_F(ShenandoahAgeCensusTest, initialize) {
8787
EXPECT_EQ(census.tenuring_threshold(), ShenandoahAgeCensus::MAX_COHORTS);
8888
}
8989

90+
TEST_F(ShenandoahAgeCensusTest, get_tenurable_bytes) {
91+
ShenandoahAgeCensus census(1);
92+
update(census);
93+
EXPECT_EQ(get_total_population_older_than(1), census.get_tenurable_bytes(1));
94+
EXPECT_LT(census.get_tenurable_bytes(2), census.get_tenurable_bytes(1));
95+
}
96+
9097
TEST_F(ShenandoahAgeCensusTest, ignore_small_populations) {
9198
// Small populations are ignored so we do not return early before reaching the youngest cohort.
9299
ShenandoahAgeCensus census(1);

0 commit comments

Comments
 (0)