Skip to content

Commit c5ead72

Browse files
authored
Choose Non-moving Policy based on features (mmtk#1308)
This PR uses an Immix space as the default non moving space, and adds two features to use mark sweep or immortal as the non moving space. * `Mutator` now includes 2 Immix allocators (one for Immix as the default space, and the other for Immix as the non moving space). * Call prepare/release/end_of_gc for the non moving space. * Add `common_prepare_func` and `common_release_func` for mutators.
1 parent 8b32d04 commit c5ead72

File tree

28 files changed

+291
-75
lines changed

28 files changed

+291
-75
lines changed

Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,17 @@ malloc_jemalloc = ["dep:jemalloc-sys"]
224224
# is not compiled in default builds.
225225
malloc_native_mimalloc = []
226226

227-
# If there are more groups, they should be inserted above this line
228-
# Group:end
229-
230227
# Group:marksweepallocation
231228
# default is native allocator with lazy sweeping
232229
eager_sweeping = []
233230
# Use library malloc as the freelist allocator for mark sweep. This will makes mark sweep slower. As malloc may return addresses outside our
234231
# normal heap range, we will have to use chunk-based SFT table. Turning on this feature will use a different SFT map implementation on 64bits,
235232
# and will affect all the plans in the build. Please be aware of the consequence, and this is only meant to be experimental use.
236233
malloc_mark_sweep = []
234+
235+
# Group:nonmovingspace
236+
immortal_as_nonmoving = []
237+
marksweep_as_nonmoving = []
238+
239+
# If there are more groups, they should be inserted above this line
237240
# Group:end

docs/userguide/src/tutorial/code/mygc_semispace/global.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ impl<VM: VMBinding> Plan for MyGC<VM> {
128128
}
129129
// ANCHOR_END: release
130130

131+
// ANCHOR: end_of_gc
132+
fn end_of_gc(&mut self, tls: VMWorkerThread) {
133+
self.common.end_of_gc(tls);
134+
}
135+
// ANCHOR_END: end_of_gc
136+
131137
// Modify
132138
// ANCHOR: plan_get_collection_reserve
133139
fn get_collection_reserved_pages(&self) -> usize {

docs/userguide/src/tutorial/mygc/ss/collection.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ with `&mygc_mutator_release`. This function will be called at the release stage
157157
allocation semantics to the new tospace. When the mutator threads resume, any new allocations for
158158
`Default` will then go to the new tospace.
159159

160+
### End of GC
161+
162+
Find the method `end_of_gc()` in `mygc/global.rs`. Call `end_of_gc` from the common plan instead.
163+
164+
```rust
165+
{{#include ../../code/mygc_semispace/global.rs:end_of_gc}}
166+
```
167+
160168
## ProcessEdgesWork for MyGC
161169

162170
[`ProcessEdgesWork`](https://docs.mmtk.io/api/mmtk/scheduler/gc_work/trait.ProcessEdgesWork.html)

src/plan/generational/copying/global.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
114114
}
115115
}
116116

117-
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
118-
self.gen
119-
.set_next_gc_full_heap(CommonGenPlan::should_next_gc_be_full_heap(self));
117+
fn end_of_gc(&mut self, tls: VMWorkerThread) {
118+
let next_gc_full_heap = CommonGenPlan::should_next_gc_be_full_heap(self);
119+
self.gen.end_of_gc(tls, next_gc_full_heap);
120120
}
121121

122122
fn get_collection_reserved_pages(&self) -> usize {

src/plan/generational/copying/mutator.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use super::GenCopy;
33
use crate::plan::barriers::ObjectBarrier;
44
use crate::plan::generational::barrier::GenObjectBarrierSemantics;
55
use crate::plan::generational::create_gen_space_mapping;
6-
use crate::plan::mutator_context::unreachable_prepare_func;
6+
use crate::plan::mutator_context::common_prepare_func;
7+
use crate::plan::mutator_context::common_release_func;
78
use crate::plan::mutator_context::Mutator;
89
use crate::plan::mutator_context::MutatorBuilder;
910
use crate::plan::mutator_context::MutatorConfig;
@@ -13,7 +14,7 @@ use crate::util::{VMMutatorThread, VMWorkerThread};
1314
use crate::vm::VMBinding;
1415
use crate::MMTK;
1516

16-
pub fn gencopy_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
17+
pub fn gencopy_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, tls: VMWorkerThread) {
1718
// reset nursery allocator
1819
let bump_allocator = unsafe {
1920
mutator
@@ -23,6 +24,8 @@ pub fn gencopy_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: V
2324
.downcast_mut::<BumpAllocator<VM>>()
2425
.unwrap();
2526
bump_allocator.reset();
27+
28+
common_release_func(mutator, tls);
2629
}
2730

2831
pub fn create_gencopy_mutator<VM: VMBinding>(
@@ -36,7 +39,7 @@ pub fn create_gencopy_mutator<VM: VMBinding>(
3639
mmtk.get_plan(),
3740
&gencopy.gen.nursery,
3841
)),
39-
prepare_func: &unreachable_prepare_func,
42+
prepare_func: &common_prepare_func,
4043
release_func: &gencopy_mutator_release,
4144
};
4245

src/plan/generational/global.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ impl<VM: VMBinding> CommonGenPlan<VM> {
7878
self.nursery.release();
7979
}
8080

81+
pub fn end_of_gc(&mut self, tls: VMWorkerThread, next_gc_full_heap: bool) {
82+
self.set_next_gc_full_heap(next_gc_full_heap);
83+
self.common.end_of_gc(tls);
84+
}
85+
8186
/// Independent of how many pages remain in the page budget (a function of heap size), we must
8287
/// ensure we never exhaust virtual memory. Therefore we must never let the nursery grow to the
8388
/// extent that it can't be copied into the mature space.

src/plan/generational/immix/global.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
131131
if full_heap {
132132
self.immix_space.prepare(
133133
full_heap,
134-
crate::policy::immix::defrag::StatsForDefrag::new(self),
134+
Some(crate::policy::immix::defrag::StatsForDefrag::new(self)),
135135
);
136136
}
137137
}
@@ -146,9 +146,9 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
146146
.store(full_heap, Ordering::Relaxed);
147147
}
148148

149-
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
150-
self.gen
151-
.set_next_gc_full_heap(CommonGenPlan::should_next_gc_be_full_heap(self));
149+
fn end_of_gc(&mut self, tls: VMWorkerThread) {
150+
let next_gc_full_heap = CommonGenPlan::should_next_gc_be_full_heap(self);
151+
self.gen.end_of_gc(tls, next_gc_full_heap);
152152

153153
let did_defrag = self.immix_space.end_of_gc();
154154
self.last_gc_was_defrag.store(did_defrag, Ordering::Relaxed);

src/plan/generational/immix/mutator.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::plan::barriers::ObjectBarrier;
33
use crate::plan::generational::barrier::GenObjectBarrierSemantics;
44
use crate::plan::generational::create_gen_space_mapping;
55
use crate::plan::generational::immix::GenImmix;
6-
use crate::plan::mutator_context::unreachable_prepare_func;
6+
use crate::plan::mutator_context::common_prepare_func;
7+
use crate::plan::mutator_context::common_release_func;
78
use crate::plan::mutator_context::Mutator;
89
use crate::plan::mutator_context::MutatorBuilder;
910
use crate::plan::mutator_context::MutatorConfig;
@@ -13,7 +14,7 @@ use crate::util::{VMMutatorThread, VMWorkerThread};
1314
use crate::vm::VMBinding;
1415
use crate::MMTK;
1516

16-
pub fn genimmix_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
17+
pub fn genimmix_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, tls: VMWorkerThread) {
1718
// reset nursery allocator
1819
let bump_allocator = unsafe {
1920
mutator
@@ -23,6 +24,8 @@ pub fn genimmix_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls:
2324
.downcast_mut::<BumpAllocator<VM>>()
2425
.unwrap();
2526
bump_allocator.reset();
27+
28+
common_release_func(mutator, tls);
2629
}
2730

2831
pub fn create_genimmix_mutator<VM: VMBinding>(
@@ -36,7 +39,7 @@ pub fn create_genimmix_mutator<VM: VMBinding>(
3639
mmtk.get_plan(),
3740
&genimmix.gen.nursery,
3841
)),
39-
prepare_func: &unreachable_prepare_func,
42+
prepare_func: &common_prepare_func,
4043
release_func: &genimmix_mutator_release,
4144
};
4245

src/plan/generational/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ pub const GEN_CONSTRAINTS: PlanConstraints = PlanConstraints {
5050
may_trace_duplicate_edges: ACTIVE_BARRIER.equals(BarrierSelector::ObjectBarrier),
5151
max_non_los_default_alloc_bytes:
5252
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
53-
needs_prepare_mutator: false,
5453
..PlanConstraints::default()
5554
};
5655

src/plan/global.rs

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ pub trait Plan: 'static + HasSpaces + Sync + Downcast {
197197

198198
/// Inform the plan about the end of a GC. It is guaranteed that there is no further work for this GC.
199199
/// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method.
200-
fn end_of_gc(&mut self, _tls: VMWorkerThread) {}
200+
fn end_of_gc(&mut self, _tls: VMWorkerThread);
201201

202202
/// Notify the plan that an emergency collection will happen. The plan should try to free as much memory as possible.
203203
/// The default implementation will force a full heap collection for generational plans.
@@ -511,6 +511,10 @@ impl<VM: VMBinding> BasePlan<VM> {
511511
self.vm_space.release();
512512
}
513513

514+
pub fn end_of_gc(&mut self, _tls: VMWorkerThread) {
515+
// Do nothing here. None of the spaces needs end_of_gc.
516+
}
517+
514518
pub(crate) fn collection_required<P: Plan>(&self, plan: &P, space_full: bool) -> bool {
515519
let stress_force_gc =
516520
crate::util::heap::gc_trigger::GCTrigger::<VM>::should_do_stress_gc_inner(
@@ -542,6 +546,17 @@ impl<VM: VMBinding> BasePlan<VM> {
542546
}
543547
}
544548

549+
cfg_if::cfg_if! {
550+
// Use immortal or mark sweep as the non moving space if the features are enabled. Otherwise use Immix.
551+
if #[cfg(feature = "immortal_as_nonmoving")] {
552+
pub type NonMovingSpace<VM> = crate::policy::immortalspace::ImmortalSpace<VM>;
553+
} else if #[cfg(feature = "marksweep_as_nonmoving")] {
554+
pub type NonMovingSpace<VM> = crate::policy::marksweepspace::native_ms::MarkSweepSpace<VM>;
555+
} else {
556+
pub type NonMovingSpace<VM> = crate::policy::immix::ImmixSpace<VM>;
557+
}
558+
}
559+
545560
/**
546561
CommonPlan is for representing state and features used by _many_ plans, but that are not fundamental to _all_ plans. Examples include the Large Object Space and an Immortal space. Features that are fundamental to _all_ plans must be included in BasePlan.
547562
*/
@@ -551,9 +566,12 @@ pub struct CommonPlan<VM: VMBinding> {
551566
pub immortal: ImmortalSpace<VM>,
552567
#[space]
553568
pub los: LargeObjectSpace<VM>,
554-
// TODO: We should use a marksweep space for nonmoving.
555569
#[space]
556-
pub nonmoving: ImmortalSpace<VM>,
570+
#[cfg_attr(
571+
not(any(feature = "immortal_as_nonmoving", feature = "marksweep_as_nonmoving")),
572+
post_scan
573+
)] // Immix space needs post_scan
574+
pub nonmoving: NonMovingSpace<VM>,
557575
#[parent]
558576
pub base: BasePlan<VM>,
559577
}
@@ -571,12 +589,7 @@ impl<VM: VMBinding> CommonPlan<VM> {
571589
args.get_space_args("los", true, false, VMRequest::discontiguous()),
572590
false,
573591
),
574-
nonmoving: ImmortalSpace::new(args.get_space_args(
575-
"nonmoving",
576-
true,
577-
false,
578-
VMRequest::discontiguous(),
579-
)),
592+
nonmoving: Self::new_nonmoving_space(&mut args),
580593
base: BasePlan::new(args),
581594
}
582595
}
@@ -591,17 +604,22 @@ impl<VM: VMBinding> CommonPlan<VM> {
591604
pub fn prepare(&mut self, tls: VMWorkerThread, full_heap: bool) {
592605
self.immortal.prepare();
593606
self.los.prepare(full_heap);
594-
self.nonmoving.prepare();
607+
self.prepare_nonmoving_space(full_heap);
595608
self.base.prepare(tls, full_heap)
596609
}
597610

598611
pub fn release(&mut self, tls: VMWorkerThread, full_heap: bool) {
599612
self.immortal.release();
600613
self.los.release(full_heap);
601-
self.nonmoving.release();
614+
self.release_nonmoving_space(full_heap);
602615
self.base.release(tls, full_heap)
603616
}
604617

618+
pub fn end_of_gc(&mut self, tls: VMWorkerThread) {
619+
self.end_of_gc_nonmoving_space();
620+
self.base.end_of_gc(tls);
621+
}
622+
605623
pub fn get_immortal(&self) -> &ImmortalSpace<VM> {
606624
&self.immortal
607625
}
@@ -610,9 +628,65 @@ impl<VM: VMBinding> CommonPlan<VM> {
610628
&self.los
611629
}
612630

613-
pub fn get_nonmoving(&self) -> &ImmortalSpace<VM> {
631+
pub fn get_nonmoving(&self) -> &NonMovingSpace<VM> {
614632
&self.nonmoving
615633
}
634+
635+
fn new_nonmoving_space(args: &mut CreateSpecificPlanArgs<VM>) -> NonMovingSpace<VM> {
636+
let space_args = args.get_space_args("nonmoving", true, false, VMRequest::discontiguous());
637+
cfg_if::cfg_if! {
638+
if #[cfg(any(feature = "immortal_as_nonmoving", feature = "marksweep_as_nonmoving"))] {
639+
NonMovingSpace::new(space_args)
640+
} else {
641+
// Immix requires extra args.
642+
NonMovingSpace::new(
643+
space_args,
644+
crate::policy::immix::ImmixSpaceArgs {
645+
unlog_object_when_traced: false,
646+
#[cfg(feature = "vo_bit")]
647+
mixed_age: false,
648+
never_move_objects: true,
649+
},
650+
)
651+
}
652+
}
653+
}
654+
655+
fn prepare_nonmoving_space(&mut self, _full_heap: bool) {
656+
cfg_if::cfg_if! {
657+
if #[cfg(feature = "immortal_as_nonmoving")] {
658+
self.nonmoving.prepare();
659+
} else if #[cfg(feature = "marksweep_as_nonmoving")] {
660+
self.nonmoving.prepare(_full_heap);
661+
} else {
662+
self.nonmoving.prepare(_full_heap, None);
663+
}
664+
}
665+
}
666+
667+
fn release_nonmoving_space(&mut self, _full_heap: bool) {
668+
cfg_if::cfg_if! {
669+
if #[cfg(feature = "immortal_as_nonmoving")] {
670+
self.nonmoving.release();
671+
} else if #[cfg(feature = "marksweep_as_nonmoving")] {
672+
self.nonmoving.prepare(_full_heap);
673+
} else {
674+
self.nonmoving.release(_full_heap);
675+
}
676+
}
677+
}
678+
679+
fn end_of_gc_nonmoving_space(&mut self) {
680+
cfg_if::cfg_if! {
681+
if #[cfg(feature = "immortal_as_nonmoving")] {
682+
// Nothing we need to do for immortal space.
683+
} else if #[cfg(feature = "marksweep_as_nonmoving")] {
684+
self.nonmoving.end_of_gc();
685+
} else {
686+
self.nonmoving.end_of_gc();
687+
}
688+
}
689+
}
616690
}
617691

618692
use crate::policy::gc_work::TraceKind;

src/plan/immix/global.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
4242
moves_objects: !cfg!(feature = "immix_non_moving"),
4343
// Max immix object size is half of a block.
4444
max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
45-
needs_prepare_mutator: false,
4645
..PlanConstraints::default()
4746
};
4847

@@ -88,7 +87,7 @@ impl<VM: VMBinding> Plan for Immix<VM> {
8887
self.common.prepare(tls, true);
8988
self.immix_space.prepare(
9089
true,
91-
crate::policy::immix::defrag::StatsForDefrag::new(self),
90+
Some(crate::policy::immix::defrag::StatsForDefrag::new(self)),
9291
);
9392
}
9493

@@ -98,9 +97,10 @@ impl<VM: VMBinding> Plan for Immix<VM> {
9897
self.immix_space.release(true);
9998
}
10099

101-
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
100+
fn end_of_gc(&mut self, tls: VMWorkerThread) {
102101
self.last_gc_was_defrag
103102
.store(self.immix_space.end_of_gc(), Ordering::Relaxed);
103+
self.common.end_of_gc(tls);
104104
}
105105

106106
fn current_gc_may_move_object(&self) -> bool {

0 commit comments

Comments
 (0)