Skip to content

Commit cb14881

Browse files
authored
Disabling PrepareMutator from PlanConstraints (#964)
Some plans do nothing when preparing mutators. We add a boolean flag so that we do not create the PrepareMutator work packets in the first place. We also remove the function that prepares Mutator for Immix. It is unnecessary to reset the ImmixAllocator of a mutator when preparing mutator because the mutator's ImmixAllocator is not used during GC. When a GC worker defragments the heap and promotes young objects, it uses the ImmixAllocator instances in ImmixCopyContext or ImmixHybridCopyContext. Fixes: #959
1 parent 15e19a1 commit cb14881

File tree

18 files changed

+82
-81
lines changed

18 files changed

+82
-81
lines changed

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

+21-21
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ the new `tospace`.
104104

105105
### Prepare mutator
106106

107-
Going back to `mutator.rs`, create a new function called
108-
`mygc_mutator_prepare(_mutator: &mut Mutator <MyGC<VM>>, _tls: OpaquePointer,)`.
109-
This function will be called at the preparation stage of a collection
110-
(at the start of a collection) for each mutator. Its body can stay empty, as
111-
there aren't any preparation steps for the mutator in this GC.
112-
In `create_mygc_mutator()`, find the field `prep_func` and change it from
113-
`mygc_mutator_noop()` to `mygc_mutator_prepare()`.
107+
Going back to `mutator.rs`, create a new function called
108+
`mygc_mutator_prepare<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread)`.
109+
This function will be called at the preparation stage of a collection (at the start of a
110+
collection) for each mutator. Its body can stay empty, as there aren't any preparation steps for
111+
the mutator in this GC. In `create_mygc_mutator()`, find the field `prepare_func` and change it
112+
from `&unreachable_prepare_func` to `&mygc_mutator_prepare`.
113+
114+
> 💡 Hint: If your plan does nothing when preparing mutators, there is an optimization you can do.
115+
You may set the plan constraints field `PlanConstraints::needs_prepare_mutator` to `false` so that
116+
the `PrepareMutator` work packets which call `prepare_func` will not be created in the first place.
117+
This optimization is helpful for VMs that run with a large number of mutator threads. If you do
118+
this optimization, you may also leave the `MutatorConfig::prepare_func` field as
119+
`&unreachable_prepare_func` to indicate it should not be called.
114120

115121
## Release
116122

@@ -131,24 +137,18 @@ routines for the common plan spaces and the fromspace.
131137

132138
### Release in mutator
133139

134-
Go back to `mutator.rs`. In `create_mygc_mutator()`, replace
135-
`mygc_mutator_noop()` in the `release_func` field with `mygc_mutator_release()`.
136-
Leave the `release()` function in the `CopyContext` empty. There are no
137-
release steps for `CopyContext` in this collector.
138-
139-
Create a new function called `mygc_mutator_release()` that takes the same
140-
inputs as the `prepare()` function above. This function will be called at the
141-
release stage of a collection (at the end of a collection) for each mutator.
142-
It rebinds the allocator for the `Default` allocation semantics to the new
143-
tospace. When the mutator threads resume, any new allocations for `Default`
144-
will then go to the new tospace.
145-
140+
Go back to `mutator.rs`. Create a new function called `mygc_mutator_release()` that takes the same
141+
inputs as the `mygc_mutator_prepare()` function above.
142+
146143
```rust
147144
{{#include ../../code/mygc_semispace/mutator.rs:release}}
148145
```
149146

150-
Delete `mygc_mutator_noop()`. It was a placeholder for the prepare and
151-
release functions that you have now added, so it is now dead code.
147+
Then go to `create_mygc_mutator()`, replace `&unreachable_release_func` in the `release_func` field
148+
with `&mygc_mutator_release`. This function will be called at the release stage of a collection
149+
(at the end of a collection) for each mutator. It rebinds the allocator for the `Default`
150+
allocation semantics to the new tospace. When the mutator threads resume, any new allocations for
151+
`Default` will then go to the new tospace.
152152

153153
## ProcessEdgesWork for MyGC
154154

src/plan/generational/copying/mutator.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ 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;
67
use crate::plan::mutator_context::Mutator;
78
use crate::plan::mutator_context::MutatorConfig;
89
use crate::plan::AllocationSemantics;
@@ -12,10 +13,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread};
1213
use crate::vm::VMBinding;
1314
use crate::MMTK;
1415

15-
pub fn gencopy_mutator_prepare<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
16-
// Do nothing
17-
}
18-
1916
pub fn gencopy_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
2017
// reset nursery allocator
2118
let bump_allocator = unsafe {
@@ -39,7 +36,7 @@ pub fn create_gencopy_mutator<VM: VMBinding>(
3936
mmtk.get_plan(),
4037
&gencopy.gen.nursery,
4138
)),
42-
prepare_func: &gencopy_mutator_prepare,
39+
prepare_func: &unreachable_prepare_func,
4340
release_func: &gencopy_mutator_release,
4441
};
4542

src/plan/generational/immix/mutator.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ 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;
67
use crate::plan::mutator_context::Mutator;
78
use crate::plan::mutator_context::MutatorConfig;
89
use crate::plan::AllocationSemantics;
@@ -12,8 +13,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread};
1213
use crate::vm::VMBinding;
1314
use crate::MMTK;
1415

15-
pub fn genimmix_mutator_prepare<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
16-
1716
pub fn genimmix_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
1817
// reset nursery allocator
1918
let bump_allocator = unsafe {
@@ -37,7 +36,7 @@ pub fn create_genimmix_mutator<VM: VMBinding>(
3736
mmtk.get_plan(),
3837
&genimmix.gen.nursery,
3938
)),
40-
prepare_func: &genimmix_mutator_prepare,
39+
prepare_func: &unreachable_prepare_func,
4140
release_func: &genimmix_mutator_release,
4241
};
4342

src/plan/generational/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub const GEN_CONSTRAINTS: PlanConstraints = PlanConstraints {
5555
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
5656
crate::util::options::NURSERY_SIZE,
5757
),
58+
needs_prepare_mutator: false,
5859
..PlanConstraints::default()
5960
};
6061

src/plan/immix/global.rs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
4343
num_specialized_scans: 1,
4444
/// Max immix object size is half of a block.
4545
max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
46+
needs_prepare_mutator: false,
4647
..PlanConstraints::default()
4748
};
4849

src/plan/immix/mutator.rs

+2-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::Immix;
22
use crate::plan::mutator_context::create_allocator_mapping;
33
use crate::plan::mutator_context::create_space_mapping;
4+
use crate::plan::mutator_context::unreachable_prepare_func;
45
use crate::plan::mutator_context::Mutator;
56
use crate::plan::mutator_context::MutatorConfig;
67
use crate::plan::mutator_context::ReservedAllocators;
@@ -15,17 +16,6 @@ use crate::{
1516
};
1617
use enum_map::EnumMap;
1718

18-
pub fn immix_mutator_prepare<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
19-
let immix_allocator = unsafe {
20-
mutator
21-
.allocators
22-
.get_allocator_mut(mutator.config.allocator_mapping[AllocationSemantics::Default])
23-
}
24-
.downcast_mut::<ImmixAllocator<VM>>()
25-
.unwrap();
26-
immix_allocator.reset();
27-
}
28-
2919
pub fn immix_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
3020
let immix_allocator = unsafe {
3121
mutator
@@ -62,7 +52,7 @@ pub fn create_immix_mutator<VM: VMBinding>(
6252
vec.push((AllocatorSelector::Immix(0), &immix.immix_space));
6353
vec
6454
}),
65-
prepare_func: &immix_mutator_prepare,
55+
prepare_func: &unreachable_prepare_func,
6656
release_func: &immix_mutator_release,
6757
};
6858

src/plan/markcompact/global.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub const MARKCOMPACT_CONSTRAINTS: PlanConstraints = PlanConstraints {
4444
needs_forward_after_liveness: true,
4545
max_non_los_default_alloc_bytes:
4646
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
47+
needs_prepare_mutator: false,
4748
..PlanConstraints::default()
4849
};
4950

src/plan/markcompact/mutator.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::MarkCompact; // Add
22
use crate::plan::barriers::NoBarrier;
33
use crate::plan::mutator_context::create_allocator_mapping;
44
use crate::plan::mutator_context::create_space_mapping;
5+
use crate::plan::mutator_context::unreachable_prepare_func;
56
use crate::plan::mutator_context::Mutator;
67
use crate::plan::mutator_context::MutatorConfig;
78
use crate::plan::mutator_context::ReservedAllocators;
@@ -38,7 +39,7 @@ pub fn create_markcompact_mutator<VM: VMBinding>(
3839
vec.push((AllocatorSelector::MarkCompact(0), markcompact.mc_space()));
3940
vec
4041
}),
41-
prepare_func: &markcompact_mutator_prepare,
42+
prepare_func: &unreachable_prepare_func,
4243
release_func: &markcompact_mutator_release,
4344
};
4445

@@ -51,12 +52,6 @@ pub fn create_markcompact_mutator<VM: VMBinding>(
5152
}
5253
}
5354

54-
pub fn markcompact_mutator_prepare<VM: VMBinding>(
55-
_mutator: &mut Mutator<VM>,
56-
_tls: VMWorkerThread,
57-
) {
58-
}
59-
6055
pub fn markcompact_mutator_release<VM: VMBinding>(
6156
_mutator: &mut Mutator<VM>,
6257
_tls: VMWorkerThread,

src/plan/marksweep/global.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub const MS_CONSTRAINTS: PlanConstraints = PlanConstraints {
4343
num_specialized_scans: 1,
4444
max_non_los_default_alloc_bytes: MAX_OBJECT_SIZE,
4545
may_trace_duplicate_edges: true,
46+
needs_prepare_mutator: !cfg!(feature = "malloc_mark_sweep")
47+
&& !cfg!(feature = "eager_sweeping"),
4648
..PlanConstraints::default()
4749
};
4850

src/plan/mutator_context.rs

+22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,28 @@ use enum_map::EnumMap;
1313

1414
pub(crate) type SpaceMapping<VM> = Vec<(AllocatorSelector, &'static dyn Space<VM>)>;
1515

16+
/// A place-holder implementation for `MutatorConfig::prepare_func` that should not be called.
17+
/// It is the most often used by plans that sets `PlanConstraints::needs_prepare_mutator` to
18+
/// `false`. It is also used by `NoGC` because it must not trigger GC.
19+
pub(crate) fn unreachable_prepare_func<VM: VMBinding>(
20+
_mutator: &mut Mutator<VM>,
21+
_tls: VMWorkerThread,
22+
) {
23+
unreachable!("`MutatorConfig::prepare_func` must not be called for the current plan.")
24+
}
25+
26+
/// A place-holder implementation for `MutatorConfig::release_func` that should not be called.
27+
/// Currently only used by `NoGC`.
28+
pub(crate) fn unreachable_release_func<VM: VMBinding>(
29+
_mutator: &mut Mutator<VM>,
30+
_tls: VMWorkerThread,
31+
) {
32+
unreachable!("`MutatorConfig::release_func` must not be called for the current plan.")
33+
}
34+
35+
/// A place-holder implementation for `MutatorConfig::release_func` that does nothing.
36+
pub(crate) fn no_op_release_func<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
37+
1638
// This struct is part of the Mutator struct.
1739
// We are trying to make it fixed-sized so that VM bindings can easily define a Mutator type to have the exact same layout as our Mutator struct.
1840
#[repr(C)]

src/plan/nogc/mutator.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::plan::barriers::NoBarrier;
2+
use crate::plan::mutator_context::unreachable_prepare_func;
3+
use crate::plan::mutator_context::unreachable_release_func;
24
use crate::plan::mutator_context::Mutator;
35
use crate::plan::mutator_context::MutatorConfig;
46
use crate::plan::mutator_context::{
@@ -8,7 +10,7 @@ use crate::plan::nogc::NoGC;
810
use crate::plan::AllocationSemantics;
911
use crate::plan::Plan;
1012
use crate::util::alloc::allocators::{AllocatorSelector, Allocators};
11-
use crate::util::{VMMutatorThread, VMWorkerThread};
13+
use crate::util::VMMutatorThread;
1214
use crate::vm::VMBinding;
1315
use enum_map::{enum_map, EnumMap};
1416

@@ -36,10 +38,6 @@ lazy_static! {
3638
};
3739
}
3840

39-
pub fn nogc_mutator_noop<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
40-
unreachable!();
41-
}
42-
4341
pub fn create_nogc_mutator<VM: VMBinding>(
4442
mutator_tls: VMMutatorThread,
4543
plan: &'static dyn Plan<VM = VM>,
@@ -62,8 +60,8 @@ pub fn create_nogc_mutator<VM: VMBinding>(
6260
));
6361
vec
6462
}),
65-
prepare_func: &nogc_mutator_noop,
66-
release_func: &nogc_mutator_noop,
63+
prepare_func: &unreachable_prepare_func,
64+
release_func: &unreachable_release_func,
6765
};
6866

6967
Mutator {

src/plan/pageprotect/global.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct PageProtect<VM: VMBinding> {
3030

3131
pub const CONSTRAINTS: PlanConstraints = PlanConstraints {
3232
moves_objects: false,
33+
needs_prepare_mutator: false,
3334
..PlanConstraints::default()
3435
};
3536

src/plan/pageprotect/mutator.rs

+5-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use super::PageProtect;
2+
use crate::plan::mutator_context::no_op_release_func;
3+
use crate::plan::mutator_context::unreachable_prepare_func;
24
use crate::plan::mutator_context::Mutator;
35
use crate::plan::mutator_context::MutatorConfig;
46
use crate::plan::mutator_context::{
@@ -8,18 +10,9 @@ use crate::plan::AllocationSemantics;
810
use crate::plan::Plan;
911
use crate::util::alloc::allocators::{AllocatorSelector, Allocators};
1012
use crate::vm::VMBinding;
11-
use crate::{
12-
plan::barriers::NoBarrier,
13-
util::opaque_pointer::{VMMutatorThread, VMWorkerThread},
14-
};
13+
use crate::{plan::barriers::NoBarrier, util::opaque_pointer::VMMutatorThread};
1514
use enum_map::EnumMap;
1615

17-
/// Prepare mutator. Do nothing.
18-
fn pp_mutator_prepare<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
19-
20-
/// Release mutator. Do nothing.
21-
fn pp_mutator_release<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
22-
2316
const RESERVED_ALLOCATORS: ReservedAllocators = ReservedAllocators {
2417
n_large_object: 1,
2518
..ReservedAllocators::DEFAULT
@@ -47,8 +40,8 @@ pub fn create_pp_mutator<VM: VMBinding>(
4740
vec.push((AllocatorSelector::LargeObject(0), &page.space));
4841
vec
4942
}),
50-
prepare_func: &pp_mutator_prepare,
51-
release_func: &pp_mutator_release,
43+
prepare_func: &unreachable_prepare_func,
44+
release_func: &no_op_release_func,
5245
};
5346

5447
Mutator {

src/plan/plan_constraints.rs

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ pub struct PlanConstraints {
3333
/// Some policies do object forwarding after the first liveness transitive closure, such as mark compact.
3434
/// For plans that use those policies, they should set this as true.
3535
pub needs_forward_after_liveness: bool,
36+
/// Some (in fact, most) plans do nothing when preparing mutators before tracing (i.e. in
37+
/// `MutatorConfig::prepare_func`). Those plans can set this to `false` so that the
38+
/// `PrepareMutator` work packets will not be created at all.
39+
pub needs_prepare_mutator: bool,
3640
}
3741

3842
impl PlanConstraints {
@@ -51,6 +55,7 @@ impl PlanConstraints {
5155
needs_forward_after_liveness: false,
5256
needs_log_bit: false,
5357
barrier: BarrierSelector::NoBarrier,
58+
needs_prepare_mutator: true,
5459
}
5560
}
5661
}

src/plan/semispace/global.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub const SS_CONSTRAINTS: PlanConstraints = PlanConstraints {
4242
num_specialized_scans: 1,
4343
max_non_los_default_alloc_bytes:
4444
crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN,
45+
needs_prepare_mutator: false,
4546
..PlanConstraints::default()
4647
};
4748

src/plan/semispace/mutator.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::SemiSpace;
22
use crate::plan::barriers::NoBarrier;
3+
use crate::plan::mutator_context::unreachable_prepare_func;
34
use crate::plan::mutator_context::Mutator;
45
use crate::plan::mutator_context::MutatorConfig;
56
use crate::plan::mutator_context::{
@@ -13,10 +14,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread};
1314
use crate::vm::VMBinding;
1415
use enum_map::EnumMap;
1516

16-
pub fn ss_mutator_prepare<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
17-
// Do nothing
18-
}
19-
2017
pub fn ss_mutator_release<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
2118
// rebind the allocation bump pointer to the appropriate semispace
2219
let bump_allocator = unsafe {
@@ -60,7 +57,7 @@ pub fn create_ss_mutator<VM: VMBinding>(
6057
vec.push((AllocatorSelector::BumpPointer(0), ss.tospace()));
6158
vec
6259
}),
63-
prepare_func: &ss_mutator_prepare,
60+
prepare_func: &unreachable_prepare_func,
6461
release_func: &ss_mutator_release,
6562
};
6663

0 commit comments

Comments
 (0)