Skip to content

Commit f27f503

Browse files
qinsoonk-sareen
authored andcommitted
Allow nursery size to be proportional to the heap size (mmtk#1087)
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 mmtk#594, but does not fully fix the problem.
1 parent 950930f commit f27f503

File tree

5 files changed

+244
-107
lines changed

5 files changed

+244
-107
lines changed

src/plan/generational/global.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,7 @@ pub struct CommonGenPlan<VM: VMBinding> {
4141
impl<VM: VMBinding> CommonGenPlan<VM> {
4242
pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> Self {
4343
let nursery = CopySpace::new(
44-
args.get_space_args(
45-
"nursery",
46-
true,
47-
VMRequest::fixed_extent(args.global_args.options.get_max_nursery_bytes(), false),
48-
),
44+
args.get_space_args("nursery", true, VMRequest::discontiguous()),
4945
true,
5046
);
5147
let full_heap_gc_count = args
@@ -115,7 +111,7 @@ impl<VM: VMBinding> CommonGenPlan<VM> {
115111
space: Option<SpaceStats<VM>>,
116112
) -> bool {
117113
let cur_nursery = self.nursery.reserved_pages();
118-
let max_nursery = self.common.base.options.get_max_nursery_pages();
114+
let max_nursery = self.common.base.gc_trigger.get_max_nursery_pages();
119115
let nursery_full = cur_nursery >= max_nursery;
120116
trace!(
121117
"nursery_full = {:?} (nursery = {}, max_nursery = {})",
@@ -278,7 +274,7 @@ impl<VM: VMBinding> CommonGenPlan<VM> {
278274
/// whose value depends on which spaces have been released.
279275
pub fn should_next_gc_be_full_heap(plan: &dyn Plan<VM = VM>) -> bool {
280276
let available = plan.get_available_pages();
281-
let min_nursery = plan.base().options.get_min_nursery_pages();
277+
let min_nursery = plan.base().gc_trigger.get_min_nursery_pages();
282278
let next_gc_full_heap = available < min_nursery;
283279
trace!(
284280
"next gc will be full heap? {}, available pages = {}, min nursery = {}",

src/plan/generational/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ pub const GEN_CONSTRAINTS: PlanConstraints = PlanConstraints {
5353
barrier: ACTIVE_BARRIER,
5454
// 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.
5555
may_trace_duplicate_edges: ACTIVE_BARRIER.equals(BarrierSelector::ObjectBarrier),
56-
max_non_los_default_alloc_bytes: crate::util::rust_util::min_of_usize(
56+
max_non_los_default_alloc_bytes:
5757
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
58-
crate::util::options::NURSERY_SIZE,
59-
),
6058
needs_prepare_mutator: false,
6159
..PlanConstraints::default()
6260
};

src/plan/sticky/immix/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
176176
}
177177

178178
fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool {
179-
let nursery_full =
180-
self.immix.immix_space.get_pages_allocated() > self.options().get_max_nursery_pages();
179+
let nursery_full = self.immix.immix_space.get_pages_allocated()
180+
> self.base().gc_trigger.get_max_nursery_pages();
181181
if space_full
182182
&& space.is_some()
183183
&& space.as_ref().unwrap().0.name() != self.immix.immix_space.name()

src/util/heap/gc_trigger.rs

+63-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use crate::global_state::GlobalState;
44
use crate::plan::gc_requester::GCRequester;
55
use crate::plan::Plan;
66
use crate::policy::space::Space;
7+
use crate::util::constants::BYTES_IN_PAGE;
78
use crate::util::conversions;
8-
use crate::util::options::{GCTriggerSelector, Options};
9+
use crate::util::options::{GCTriggerSelector, Options, DEFAULT_MAX_NURSERY, DEFAULT_MIN_NURSERY};
910
use crate::vm::VMBinding;
1011
use crate::MMTK;
1112
use std::mem::MaybeUninit;
@@ -58,6 +59,10 @@ impl<VM: VMBinding> GCTrigger<VM> {
5859
self.plan.write(plan);
5960
}
6061

62+
fn plan(&self) -> &dyn Plan<VM = VM> {
63+
unsafe { self.plan.assume_init() }
64+
}
65+
6166
/// This method is called periodically by the allocation subsystem
6267
/// (by default, each time a page is consumed), and provides the
6368
/// collector with an opportunity to collect.
@@ -101,8 +106,62 @@ impl<VM: VMBinding> GCTrigger<VM> {
101106

102107
/// Check if the heap is full
103108
pub fn is_heap_full(&self) -> bool {
104-
let plan = unsafe { self.plan.assume_init() };
105-
self.policy.is_heap_full(plan)
109+
self.policy.is_heap_full(self.plan())
110+
}
111+
112+
/// Return upper bound of the nursery size (in number of bytes)
113+
pub fn get_max_nursery_bytes(&self) -> usize {
114+
use crate::util::options::NurserySize;
115+
debug_assert!(self.plan().generational().is_some());
116+
match *self.options.nursery {
117+
NurserySize::Bounded { min: _, max } => max,
118+
NurserySize::ProportionalBounded { min: _, max } => {
119+
let heap_size_bytes =
120+
conversions::pages_to_bytes(self.policy.get_current_heap_size_in_pages());
121+
let max_bytes = heap_size_bytes as f64 * max;
122+
let max_bytes = conversions::raw_align_up(max_bytes as usize, BYTES_IN_PAGE);
123+
if max_bytes > DEFAULT_MAX_NURSERY {
124+
warn!("Proportional nursery with max size {} ({}) is larger than DEFAULT_MAX_NURSERY ({}). Use DEFAULT_MAX_NURSERY instead.", max, max_bytes, DEFAULT_MAX_NURSERY);
125+
DEFAULT_MAX_NURSERY
126+
} else {
127+
max_bytes
128+
}
129+
}
130+
NurserySize::Fixed(sz) => sz,
131+
}
132+
}
133+
134+
/// Return lower bound of the nursery size (in number of bytes)
135+
pub fn get_min_nursery_bytes(&self) -> usize {
136+
use crate::util::options::NurserySize;
137+
debug_assert!(self.plan().generational().is_some());
138+
match *self.options.nursery {
139+
NurserySize::Bounded { min, max: _ } => min,
140+
NurserySize::ProportionalBounded { min, max: _ } => {
141+
let min_bytes =
142+
conversions::pages_to_bytes(self.policy.get_current_heap_size_in_pages())
143+
as f64
144+
* min;
145+
let min_bytes = conversions::raw_align_up(min_bytes as usize, BYTES_IN_PAGE);
146+
if min_bytes < DEFAULT_MIN_NURSERY {
147+
warn!("Proportional nursery with min size {} ({}) is smaller than DEFAULT_MIN_NURSERY ({}). Use DEFAULT_MIN_NURSERY instead.", min, min_bytes, DEFAULT_MIN_NURSERY);
148+
DEFAULT_MIN_NURSERY
149+
} else {
150+
min_bytes
151+
}
152+
}
153+
NurserySize::Fixed(sz) => sz,
154+
}
155+
}
156+
157+
/// Return upper bound of the nursery size (in number of pages)
158+
pub fn get_max_nursery_pages(&self) -> usize {
159+
crate::util::conversions::bytes_to_pages_up(self.get_max_nursery_bytes())
160+
}
161+
162+
/// Return lower bound of the nursery size (in number of pages)
163+
pub fn get_min_nursery_pages(&self) -> usize {
164+
crate::util::conversions::bytes_to_pages_up(self.get_min_nursery_bytes())
106165
}
107166

108167
pub fn clamp_max_heap_size(&mut self, max: usize) -> bool {
@@ -443,7 +502,7 @@ impl<VM: VMBinding> GCTriggerPolicy<VM> for MemBalancerTrigger {
443502
// We reserve an extra of min nursery. This ensures that we will not trigger
444503
// a full heap GC in the next GC (if available pages is smaller than min nursery, we will force a full heap GC)
445504
mmtk.get_plan().get_collection_reserved_pages()
446-
+ mmtk.options.get_min_nursery_pages(),
505+
+ mmtk.gc_trigger.get_min_nursery_pages(),
447506
stats,
448507
);
449508
}

0 commit comments

Comments
 (0)