Skip to content

Commit

Permalink
Allow nursery size to be proportional to the heap size (#1087)
Browse files Browse the repository at this point in the history
This PR introduces different kinds of nursery size options, and by
default, we use a proportion of the heap size as the min nursery. This
PR should generally improve the generational plans' performance by
triggering a full heap GC more promptly. This PR mitigates the issue
identified in #594, but does not
fully fix the problem.
  • Loading branch information
qinsoon authored Apr 17, 2024
1 parent a650e87 commit 79cfcb6
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 107 deletions.
10 changes: 3 additions & 7 deletions src/plan/generational/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ pub struct CommonGenPlan<VM: VMBinding> {
impl<VM: VMBinding> CommonGenPlan<VM> {
pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> Self {
let nursery = CopySpace::new(
args.get_space_args(
"nursery",
true,
VMRequest::fixed_extent(args.global_args.options.get_max_nursery_bytes(), false),
),
args.get_space_args("nursery", true, VMRequest::discontiguous()),
true,
);
let full_heap_gc_count = args
Expand Down Expand Up @@ -102,7 +98,7 @@ impl<VM: VMBinding> CommonGenPlan<VM> {
space: Option<SpaceStats<VM>>,
) -> bool {
let cur_nursery = self.nursery.reserved_pages();
let max_nursery = self.common.base.options.get_max_nursery_pages();
let max_nursery = self.common.base.gc_trigger.get_max_nursery_pages();
let nursery_full = cur_nursery >= max_nursery;
trace!(
"nursery_full = {:?} (nursery = {}, max_nursery = {})",
Expand Down Expand Up @@ -261,7 +257,7 @@ impl<VM: VMBinding> CommonGenPlan<VM> {
/// whose value depends on which spaces have been released.
pub fn should_next_gc_be_full_heap(plan: &dyn Plan<VM = VM>) -> bool {
let available = plan.get_available_pages();
let min_nursery = plan.base().options.get_min_nursery_pages();
let min_nursery = plan.base().gc_trigger.get_min_nursery_pages();
let next_gc_full_heap = available < min_nursery;
trace!(
"next gc will be full heap? {}, available pages = {}, min nursery = {}",
Expand Down
4 changes: 1 addition & 3 deletions src/plan/generational/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ pub const GEN_CONSTRAINTS: PlanConstraints = PlanConstraints {
barrier: ACTIVE_BARRIER,
// We may trace duplicate edges in sticky immix (or any plan that uses object remembering barrier). See https://github.com/mmtk/mmtk-core/issues/743.
may_trace_duplicate_edges: ACTIVE_BARRIER.equals(BarrierSelector::ObjectBarrier),
max_non_los_default_alloc_bytes: crate::util::rust_util::min_of_usize(
max_non_los_default_alloc_bytes:
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
crate::util::options::NURSERY_SIZE,
),
needs_prepare_mutator: false,
..PlanConstraints::default()
};
Expand Down
4 changes: 2 additions & 2 deletions src/plan/sticky/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
}

fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool {
let nursery_full =
self.immix.immix_space.get_pages_allocated() > self.options().get_max_nursery_pages();
let nursery_full = self.immix.immix_space.get_pages_allocated()
> self.base().gc_trigger.get_max_nursery_pages();
if space_full
&& space.is_some()
&& space.as_ref().unwrap().0.name() != self.immix.immix_space.name()
Expand Down
67 changes: 63 additions & 4 deletions src/util/heap/gc_trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use crate::global_state::GlobalState;
use crate::plan::gc_requester::GCRequester;
use crate::plan::Plan;
use crate::policy::space::Space;
use crate::util::constants::BYTES_IN_PAGE;
use crate::util::conversions;
use crate::util::options::{GCTriggerSelector, Options};
use crate::util::options::{GCTriggerSelector, Options, DEFAULT_MAX_NURSERY, DEFAULT_MIN_NURSERY};
use crate::vm::VMBinding;
use crate::MMTK;
use std::mem::MaybeUninit;
Expand Down Expand Up @@ -58,6 +59,10 @@ impl<VM: VMBinding> GCTrigger<VM> {
self.plan.write(plan);
}

fn plan(&self) -> &dyn Plan<VM = VM> {
unsafe { self.plan.assume_init() }
}

/// This method is called periodically by the allocation subsystem
/// (by default, each time a page is consumed), and provides the
/// collector with an opportunity to collect.
Expand Down Expand Up @@ -101,8 +106,62 @@ impl<VM: VMBinding> GCTrigger<VM> {

/// Check if the heap is full
pub fn is_heap_full(&self) -> bool {
let plan = unsafe { self.plan.assume_init() };
self.policy.is_heap_full(plan)
self.policy.is_heap_full(self.plan())
}

/// Return upper bound of the nursery size (in number of bytes)
pub fn get_max_nursery_bytes(&self) -> usize {
use crate::util::options::NurserySize;
debug_assert!(self.plan().generational().is_some());
match *self.options.nursery {
NurserySize::Bounded { min: _, max } => max,
NurserySize::ProportionalBounded { min: _, max } => {
let heap_size_bytes =
conversions::pages_to_bytes(self.policy.get_current_heap_size_in_pages());
let max_bytes = heap_size_bytes as f64 * max;
let max_bytes = conversions::raw_align_up(max_bytes as usize, BYTES_IN_PAGE);
if max_bytes > DEFAULT_MAX_NURSERY {
warn!("Proportional nursery with max size {} ({}) is larger than DEFAULT_MAX_NURSERY ({}). Use DEFAULT_MAX_NURSERY instead.", max, max_bytes, DEFAULT_MAX_NURSERY);
DEFAULT_MAX_NURSERY
} else {
max_bytes
}
}
NurserySize::Fixed(sz) => sz,
}
}

/// Return lower bound of the nursery size (in number of bytes)
pub fn get_min_nursery_bytes(&self) -> usize {
use crate::util::options::NurserySize;
debug_assert!(self.plan().generational().is_some());
match *self.options.nursery {
NurserySize::Bounded { min, max: _ } => min,
NurserySize::ProportionalBounded { min, max: _ } => {
let min_bytes =
conversions::pages_to_bytes(self.policy.get_current_heap_size_in_pages())
as f64
* min;
let min_bytes = conversions::raw_align_up(min_bytes as usize, BYTES_IN_PAGE);
if min_bytes < DEFAULT_MIN_NURSERY {
warn!("Proportional nursery with min size {} ({}) is smaller than DEFAULT_MIN_NURSERY ({}). Use DEFAULT_MIN_NURSERY instead.", min, min_bytes, DEFAULT_MIN_NURSERY);
DEFAULT_MIN_NURSERY
} else {
min_bytes
}
}
NurserySize::Fixed(sz) => sz,
}
}

/// Return upper bound of the nursery size (in number of pages)
pub fn get_max_nursery_pages(&self) -> usize {
crate::util::conversions::bytes_to_pages_up(self.get_max_nursery_bytes())
}

/// Return lower bound of the nursery size (in number of pages)
pub fn get_min_nursery_pages(&self) -> usize {
crate::util::conversions::bytes_to_pages_up(self.get_min_nursery_bytes())
}
}

Expand Down Expand Up @@ -433,7 +492,7 @@ impl<VM: VMBinding> GCTriggerPolicy<VM> for MemBalancerTrigger {
// We reserve an extra of min nursery. This ensures that we will not trigger
// a full heap GC in the next GC (if available pages is smaller than min nursery, we will force a full heap GC)
mmtk.get_plan().get_collection_reserved_pages()
+ mmtk.options.get_min_nursery_pages(),
+ mmtk.gc_trigger.get_min_nursery_pages(),
stats,
);
}
Expand Down
Loading

0 comments on commit 79cfcb6

Please sign in to comment.