Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8327097: GenShen: Align PLAB sizes down rather than up #401

Closed
Closed
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7bb1d38
Remove dead code for inelastic plabs
kdnilsen Feb 26, 2024
8bc4367
Revert "Remove dead code for inelastic plabs"
kdnilsen Feb 26, 2024
99cce53
Round LAB sizes down rather than up to force alignment
kdnilsen Feb 26, 2024
11b26bb
Revert "Round LAB sizes down rather than up to force alignment"
kdnilsen Feb 26, 2024
809ecf2
Make TestHumongousThreshold.java run faster
kdnilsen Feb 27, 2024
28d781a
Use existing align_down macros instead of hand-written code
kdnilsen Feb 27, 2024
2a1b1b8
Fix typo in test program
kdnilsen Feb 27, 2024
4b78db9
Revert "Fix typo in test program"
kdnilsen Feb 27, 2024
f36ef80
Revert "Make TestHumongousThreshold.java run faster"
kdnilsen Feb 27, 2024
1cbcec5
Remove over-zealous assertion
kdnilsen Feb 27, 2024
941d8aa
Merge branch 'openjdk:master' into master
kdnilsen Feb 27, 2024
07598d8
Merge remote-tracking branch 'origin/master' into enforce-humongous-t…
kdnilsen Feb 27, 2024
259e3ea
Disable TestHumongousThreshold.java test on macos-aarch64
kdnilsen Feb 29, 2024
56572f8
Fix two typos in Problem List
kdnilsen Feb 29, 2024
4c912bc
Try a broader exclusion for TestHumongousThreshold problem list
kdnilsen Feb 29, 2024
7b171fe
Forcefully disable TestHumongousThreshold.java tier2 test
kdnilsen Mar 2, 2024
6e7ac87
Fix up exclude declaration for TestHumongousThreshold.java
kdnilsen Mar 4, 2024
3aba430
Fix disabling of TestHumongousThreshold on macosx-aarch64
kdnilsen Mar 5, 2024
50fb3e3
Assure PLAB alignment by construction rather than fixup
kdnilsen Mar 19, 2024
1ae9b1c
Add comment to explain requirement for PLAB alignment
kdnilsen Mar 19, 2024
fac72cb
Cleanup code in response to reviewer feedback
kdnilsen Mar 19, 2024
e094aa0
Disable TestHumongousThreshold test on generic-all for now
kdnilsen Mar 19, 2024
5de1881
Force size to align up
kdnilsen Mar 19, 2024
45695f0
Do not align_up size of object allocated in plab slow
kdnilsen Mar 20, 2024
66609d9
Add comments to clarify behavior of retire_plab
kdnilsen Mar 20, 2024
7766351
Fix up a few other places where PLAB min size or current size gets set
kdnilsen Mar 21, 2024
1e28350
Respond to reviewer feedback
kdnilsen Mar 22, 2024
b0cba3e
Merge branch 'master' of https://git.openjdk.org/shenandoah into enfo…
kdnilsen Mar 22, 2024
2a4c62c
Fix up initialization of min_plab_size and max_plab_size
kdnilsen Mar 22, 2024
2cb710d
Add include to resolve undeclared enum
kdnilsen Mar 22, 2024
ecd6615
Add detail to log message to help with debugging
kdnilsen Mar 23, 2024
780f10f
Tighten up assert to assist with debugging
kdnilsen Mar 23, 2024
6d82e4b
Bug fixes to address regressions encountered during integration testing
kdnilsen Mar 24, 2024
fdc8f22
Add TODO comment for future refactoring of _plab initialization
kdnilsen Mar 25, 2024
71d9dce
Add a missing include to fix zero build
kdnilsen Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -29,6 +29,9 @@
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
#include "logging/log.hpp"
#include "utilities/quickSort.hpp"

uint ShenandoahOldHeuristics::NOT_FOUND = -1U;
6 changes: 4 additions & 2 deletions src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
Original file line number Diff line number Diff line change
@@ -217,8 +217,10 @@ void ShenandoahArguments::initialize_alignments() {
}

CollectedHeap* ShenandoahArguments::create_heap() {
if (strcmp(ShenandoahGCMode, "generational") == 0) {
if (strcmp(ShenandoahGCMode, "generational") != 0) {
// Not generational
return new ShenandoahHeap(new ShenandoahCollectorPolicy());
} else {
return new ShenandoahGenerationalHeap(new ShenandoahCollectorPolicy());
}
return new ShenandoahHeap(new ShenandoahCollectorPolicy());
}
9 changes: 5 additions & 4 deletions src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
Original file line number Diff line number Diff line change
@@ -459,10 +459,11 @@ HeapWord* ShenandoahFreeSet::allocate_old_with_affiliation(ShenandoahAffiliation

void ShenandoahFreeSet::add_old_collector_free_region(ShenandoahHeapRegion* region) {
shenandoah_assert_heaplocked();
size_t plab_min_size_in_bytes = ShenandoahGenerationalHeap::heap()->plab_min_size() * HeapWordSize;
size_t idx = region->index();
size_t capacity = alloc_capacity(region);
assert(_free_sets.membership(idx) == NotFree, "Regions promoted in place should not be in any free set");
if (capacity >= PLAB::min_size() * HeapWordSize) {
if (capacity >= plab_min_size_in_bytes) {
_free_sets.make_free(idx, OldCollector, capacity);
_heap->old_generation()->augment_promoted_reserve(capacity);
}
@@ -698,13 +699,13 @@ HeapWord* ShenandoahFreeSet::allocate_aligned_plab(size_t size, ShenandoahAllocR
assert(_heap->mode()->is_generational(), "PLABs are only for generational mode");
assert(r->is_old(), "All PLABs reside in old-gen");
assert(!req.is_mutator_alloc(), "PLABs should not be allocated by mutators.");
assert(size % CardTable::card_size_in_words() == 0, "size must be multiple of card table size, was " SIZE_FORMAT, size);
assert(is_aligned(size, CardTable::card_size_in_words()), "Align by design");

HeapWord* result = r->allocate_aligned(size, req, CardTable::card_size());
assert(result != nullptr, "Allocation cannot fail");
assert(r->top() <= r->end(), "Allocation cannot span end of region");
assert(req.actual_size() == size, "Should not have needed to adjust size for PLAB.");
assert(((uintptr_t) result) % CardTable::card_size_in_words() == 0, "PLAB start must align with card boundary");
assert(is_aligned(result, CardTable::card_size_in_words()), "Align by design");

return result;
}
@@ -1066,7 +1067,7 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_cset_regi
assert(!region->is_cset(), "Shouldn't be adding cset regions to the free set");
assert(_free_sets.in_free_set(idx, NotFree), "We are about to make region free; it should not be free already");

// Do not add regions that would almost surely fail allocation
// Do not add regions that would almost surely fail allocation. Note that PLAB::min_size() is typically less than ShenandoahGenerationalHeap::plab_min_size()
if (alloc_capacity(region) < PLAB::min_size() * HeapWordSize) continue;

if (region->is_old()) {
20 changes: 18 additions & 2 deletions src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
Original file line number Diff line number Diff line change
@@ -72,9 +72,25 @@ ShenandoahGenerationalHeap* ShenandoahGenerationalHeap::heap() {
return checked_cast<ShenandoahGenerationalHeap*>(heap);
}

size_t ShenandoahGenerationalHeap::calculate_min_plab() const {
return align_up(PLAB::min_size(), CardTable::card_size_in_words());
}

size_t ShenandoahGenerationalHeap::calculate_max_plab() const {
size_t MaxTLABSizeWords = ShenandoahHeapRegion::max_tlab_size_words();
return ((ShenandoahMaxEvacLABRatio > 0)?
align_down(MIN2(MaxTLABSizeWords, PLAB::min_size() * ShenandoahMaxEvacLABRatio), CardTable::card_size_in_words()):
align_down(MaxTLABSizeWords, CardTable::card_size_in_words()));
}

ShenandoahGenerationalHeap::ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy) :
ShenandoahHeap(policy),
_regulator_thread(nullptr) { }
_min_plab_size(calculate_min_plab()),
_max_plab_size(calculate_max_plab()),
_regulator_thread(nullptr) {
assert(is_aligned(_min_plab_size, CardTable::card_size_in_words()), "min_plab_size must be aligned");
assert(is_aligned(_max_plab_size, CardTable::card_size_in_words()), "max_plab_size must be aligned");
}

void ShenandoahGenerationalHeap::print_init_logger() const {
ShenandoahGenerationalInitLogger logger;
@@ -265,4 +281,4 @@ void ShenandoahGenerationalHeap::TransferResult::print_on(const char* when, outp
when,
success? "successfully transferred": "failed to transfer", region_count, region_destination,
PROPERFMTARGS(old_available), PROPERFMTARGS(young_available));
}
}
11 changes: 11 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
Original file line number Diff line number Diff line change
@@ -31,11 +31,22 @@ class ShenandoahRegulatorThread;
class ShenandoahGenerationalControlThread;

class ShenandoahGenerationalHeap : public ShenandoahHeap {
private:
const size_t _min_plab_size;
const size_t _max_plab_size;

size_t calculate_min_plab() const;
size_t calculate_max_plab() const;

public:
explicit ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy);


static ShenandoahGenerationalHeap* heap();

inline size_t plab_min_size() const { return _min_plab_size; }
inline size_t plab_max_size() const { return _max_plab_size; }

void print_init_logger() const override;

// ---------- Serviceability
77 changes: 41 additions & 36 deletions src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
#include "gc/shenandoah/shenandoahControlThread.hpp"
#include "gc/shenandoah/shenandoahFreeSet.hpp"
#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
#include "gc/shenandoah/shenandoahGlobalGeneration.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
@@ -298,7 +299,7 @@ jint ShenandoahHeap::initialize() {
_bitmap_region_special = bitmap.special();

size_t bitmap_init_commit = _bitmap_bytes_per_slice *
align_up(num_committed_regions, _bitmap_regions_per_slice) / _bitmap_regions_per_slice;
align_up(num_committed_regions, _bitmap_regions_per_slice) / _bitmap_regions_per_slice;
bitmap_init_commit = MIN2(_bitmap_size, bitmap_init_commit);
if (!_bitmap_region_special) {
os::commit_memory_or_exit((char *) _bitmap_region.start(), bitmap_init_commit, bitmap_page_size, false,
@@ -1024,26 +1025,27 @@ HeapWord* ShenandoahHeap::allocate_from_gclab_slow(Thread* thread, size_t size)
// Establish a new PLAB and allocate size HeapWords within it.
HeapWord* ShenandoahHeap::allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion) {
// New object should fit the PLAB size
size_t min_size = MAX2(size, PLAB::min_size());

// Figure out size of new PLAB, looking back at heuristics. Expand aggressively.
assert(mode()->is_generational(), "PLABs only relevant to generational GC");
ShenandoahGenerationalHeap* generational_heap = (ShenandoahGenerationalHeap*) this;
const size_t plab_min_size = generational_heap->plab_min_size();
const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size;

// Figure out size of new PLAB, looking back at heuristics. Expand aggressively. PLABs must align on size
// of card table in order to avoid the need for synchronization when registering newly allocated objects within
// the card table.
size_t cur_size = ShenandoahThreadLocalData::plab_size(thread);
if (cur_size == 0) {
cur_size = PLAB::min_size();
}
size_t future_size = cur_size * 2;
// Limit growth of PLABs to ShenandoahMaxEvacLABRatio * the minimum size. This enables more equitable distribution of
// available evacuation buidget between the many threads that are coordinating in the evacuation effort.
if (ShenandoahMaxEvacLABRatio > 0) {
future_size = MIN2(future_size, PLAB::min_size() * ShenandoahMaxEvacLABRatio);
cur_size = plab_min_size;
}
future_size = MIN2(future_size, PLAB::max_size());
future_size = MAX2(future_size, PLAB::min_size());

size_t unalignment = future_size % CardTable::card_size_in_words();
if (unalignment != 0) {
future_size = future_size - unalignment + CardTable::card_size_in_words();
}
// Limit growth of PLABs to the smaller of ShenandoahMaxEvacLABRatio * the minimum size and ShenandoahHumongousThreshold.
// This minimum value is represented by generational_heap->plab_max_size(). Enforcing this limit enables more equitable
// distribution of available evacuation budget between the many threads that are coordinating in the evacuation effort.
size_t future_size = MIN2(cur_size * 2, generational_heap->plab_max_size());
assert(is_aligned(future_size, CardTable::card_size_in_words()), "Align by design, future_size: " SIZE_FORMAT
", alignment: " SIZE_FORMAT ", cur_size: " SIZE_FORMAT ", max: " SIZE_FORMAT,
future_size, (size_t) CardTable::card_size_in_words(), cur_size, generational_heap->plab_max_size());

// Record new heuristic value even if we take any shortcut. This captures
// the case when moderately-sized objects always take a shortcut. At some point,
@@ -1058,7 +1060,7 @@ HeapWord* ShenandoahHeap::allocate_from_plab_slow(Thread* thread, size_t size, b

// Retire current PLAB, and allocate a new one.
PLAB* plab = ShenandoahThreadLocalData::plab(thread);
if (plab->words_remaining() < PLAB::min_size()) {
if (plab->words_remaining() < plab_min_size) {
// Retire current PLAB, and allocate a new one.
// CAUTION: retire_plab may register the remnant filler object with the remembered set scanner without a lock. This
// is safe iff it is assured that each PLAB is a whole-number multiple of card-mark memory size and each PLAB is
@@ -1070,7 +1072,7 @@ HeapWord* ShenandoahHeap::allocate_from_plab_slow(Thread* thread, size_t size, b
// less than the remaining evacuation need. It also adjusts plab_preallocated and expend_promoted if appropriate.
HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size);
if (plab_buf == nullptr) {
if (min_size == PLAB::min_size()) {
if (min_size == plab_min_size) {
// Disable plab promotions for this thread because we cannot even allocate a plab of minimal size. This allows us
// to fail faster on subsequent promotion attempts.
ShenandoahThreadLocalData::disable_plab_promotions(thread);
@@ -1079,7 +1081,7 @@ HeapWord* ShenandoahHeap::allocate_from_plab_slow(Thread* thread, size_t size, b
} else {
ShenandoahThreadLocalData::enable_plab_retries(thread);
}
assert (size <= actual_size, "allocation should fit");
// Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail.
if (ZeroTLAB) {
// ..and clear it.
Copy::zero_to_words(plab_buf, actual_size);
@@ -1093,6 +1095,7 @@ HeapWord* ShenandoahHeap::allocate_from_plab_slow(Thread* thread, size_t size, b
Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal);
#endif // ASSERT
}
assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design");
plab->set_buf(plab_buf, actual_size);
if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) {
return nullptr;
@@ -1129,15 +1132,17 @@ void ShenandoahHeap::retire_plab(PLAB* plab, Thread* thread) {
if (not_promoted > 0) {
old_generation()->unexpend_promoted(not_promoted);
}
size_t waste = plab->waste();
HeapWord* top = plab->top();
const size_t original_waste = plab->waste();
HeapWord* const top = plab->top();

// plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable.
// It adds the size of this unused memory, in words, to plab->waste().
plab->retire();
if (top != nullptr && plab->waste() > waste && is_in_old(top)) {
// If retiring the plab created a filler object, then we
// need to register it with our card scanner so it can
if (top != nullptr && plab->waste() > original_waste && is_in_old(top)) {
// If retiring the plab created a filler object, then we need to register it with our card scanner so it can
// safely walk the region backing the plab.
log_debug(gc)("retire_plab() is registering remnant of size " SIZE_FORMAT " at " PTR_FORMAT,
plab->waste() - waste, p2i(top));
plab->waste() - original_waste, p2i(top));
card_scan()->register_object_without_lock(top);
}
}
@@ -1195,14 +1200,11 @@ HeapWord* ShenandoahHeap::allocate_new_gclab(size_t min_size,
return res;
}

HeapWord* ShenandoahHeap::allocate_new_plab(size_t min_size,
size_t word_size,
size_t* actual_size) {
// Align requested sizes to card sized multiples
size_t words_in_card = CardTable::card_size_in_words();
size_t align_mask = ~(words_in_card - 1);
min_size = (min_size + words_in_card - 1) & align_mask;
word_size = (word_size + words_in_card - 1) & align_mask;
HeapWord* ShenandoahHeap::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) {
// Align requested sizes to card-sized multiples. Align down so that we don't violate max size of TLAB.
assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design");
assert(word_size >= min_size, "Requested PLAB is too small");

ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size);
// Note that allocate_memory() sets a thread-local flag to prohibit further promotions by this thread
// if we are at risk of infringing on the old-gen evacuation budget.
@@ -1212,6 +1214,7 @@ HeapWord* ShenandoahHeap::allocate_new_plab(size_t min_size,
} else {
*actual_size = 0;
}
assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design");
return res;
}

@@ -1723,6 +1726,8 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
break;
}
case OLD_GENERATION: {
assert(mode()->is_generational(), "OLD Generation only exists in generational mode");
ShenandoahGenerationalHeap* gen_heap = (ShenandoahGenerationalHeap*) this;
PLAB* plab = ShenandoahThreadLocalData::plab(thread);
if (plab != nullptr) {
has_plab = true;
@@ -1732,13 +1737,13 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
ShenandoahThreadLocalData::plab_retries_enabled(thread)) {
// PLAB allocation failed because we are bumping up against the limit on old evacuation reserve or because
// the requested object does not fit within the current plab but the plab still has an "abundance" of memory,
// where abundance is defined as >= PLAB::min_size(). In the former case, we try resetting the desired
// where abundance is defined as >= ShenGenHeap::plab_min_size(). In the former case, we try resetting the desired
// PLAB size and retry PLAB allocation to avoid cascading of shared memory allocations.

// In this situation, PLAB memory is precious. We'll try to preserve our existing PLAB by forcing
// this particular allocation to be shared.
if (plab->words_remaining() < PLAB::min_size()) {
ShenandoahThreadLocalData::set_plab_size(thread, PLAB::min_size());
if (plab->words_remaining() < gen_heap->plab_min_size()) {
ShenandoahThreadLocalData::set_plab_size(thread, gen_heap->plab_min_size());
copy = allocate_from_plab(thread, size, is_promotion);
// If we still get nullptr, we'll try a shared allocation below.
if (copy == nullptr) {
4 changes: 2 additions & 2 deletions src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
Original file line number Diff line number Diff line change
@@ -313,11 +313,11 @@ inline HeapWord* ShenandoahHeap::allocate_from_plab(Thread* thread, size_t size,
}
// if plab->word_size() <= 0, thread's plab not yet initialized for this pass, so allow_plab_promotions() is not trustworthy
obj = plab->allocate(size);
if ((obj == nullptr) && (plab->words_remaining() < PLAB::min_size())) {
if ((obj == nullptr) && (plab->words_remaining() < ShenandoahGenerationalHeap::heap()->plab_min_size())) {
// allocate_from_plab_slow will establish allow_plab_promotions(thread) for future invocations
obj = allocate_from_plab_slow(thread, size, is_promotion);
}
// if plab->words_remaining() >= PLAB::min_size(), just return nullptr so we can use a shared allocation
// if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation
if (obj == nullptr) {
return nullptr;
}
2 changes: 1 addition & 1 deletion src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
Original file line number Diff line number Diff line change
@@ -1064,7 +1064,7 @@ void ShenandoahHeapRegion::promote_in_place() {
old_gen->increment_affiliated_region_count();
old_gen->increase_used(region_used);

// add_old_collector_free_region() increases promoted_reserve() if available space exceeds PLAB::min_size()
// add_old_collector_free_region() increases promoted_reserve() if available space exceeds plab_min_size()
heap->free_set()->add_old_collector_free_region(this);
}
}
7 changes: 5 additions & 2 deletions src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
Original file line number Diff line number Diff line change
@@ -351,8 +351,11 @@ class ShenandoahHeapRegion {
inline void restore_top_before_promote();
inline size_t garbage_before_padded_for_promote() const;

// Allocation (return nullptr if full)
inline HeapWord* allocate_aligned(size_t word_size, ShenandoahAllocRequest &req, size_t alignment_in_words);
// If next available memory is not aligned on address that is multiple of alignment, fill the empty space
// so that returned object is aligned on an address that is a multiple of alignment_in_bytes. Requested
// size is in words. It is assumed that this->is_old(). A pad object is allocated, filled, and registered
// if necessary to assure the new allocation is properly aligned. Return nullptr if memory is not available.
inline HeapWord* allocate_aligned(size_t word_size, ShenandoahAllocRequest &req, size_t alignment_in_bytes);

// Allocation (return nullptr if full)
inline HeapWord* allocate(size_t word_size, ShenandoahAllocRequest req);
Loading