Skip to content

Allow configuring each Immix space to be non moving #1305

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions src/plan/generational/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {

fn last_collection_was_exhaustive(&self) -> bool {
self.last_gc_was_full_heap.load(Ordering::Relaxed)
&& ImmixSpace::<VM>::is_last_gc_exhaustive(
self.last_gc_was_defrag.load(Ordering::Relaxed),
)
&& self
.immix_space
.is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
}

fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool
Expand Down Expand Up @@ -254,6 +254,7 @@ impl<VM: VMBinding> GenImmix<VM> {
// In GenImmix, young objects are not allocated in ImmixSpace directly.
#[cfg(feature = "vo_bit")]
mixed_age: false,
never_move_objects: false,
},
);

Expand Down
7 changes: 5 additions & 2 deletions src/plan/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pub struct Immix<VM: VMBinding> {

/// The plan constraints for the immix plan.
pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
moves_objects: crate::policy::immix::DEFRAG,
// If we disable moving in Immix, this is a non-moving plan.
moves_objects: !cfg!(feature = "immix_non_moving"),
// Max immix object size is half of a block.
max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
needs_prepare_mutator: false,
Expand All @@ -51,7 +52,8 @@ impl<VM: VMBinding> Plan for Immix<VM> {
}

fn last_collection_was_exhaustive(&self) -> bool {
ImmixSpace::<VM>::is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
self.immix_space
.is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
}

fn constraints(&self) -> &'static PlanConstraints {
Expand Down Expand Up @@ -139,6 +141,7 @@ impl<VM: VMBinding> Immix<VM> {
unlog_object_when_traced: false,
#[cfg(feature = "vo_bit")]
mixed_age: false,
never_move_objects: false,
},
)
}
Expand Down
9 changes: 5 additions & 4 deletions src/plan/sticky/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::plan::PlanConstraints;
use crate::policy::gc_work::TraceKind;
use crate::policy::gc_work::TRACE_KIND_TRANSITIVE_PIN;
use crate::policy::immix::ImmixSpace;
use crate::policy::immix::PREFER_COPY_ON_NURSERY_GC;
use crate::policy::immix::TRACE_KIND_FAST;
use crate::policy::sft::SFT;
use crate::policy::space::Space;
Expand Down Expand Up @@ -41,7 +40,8 @@ pub struct StickyImmix<VM: VMBinding> {

/// The plan constraints for the sticky immix plan.
pub const STICKY_IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
moves_objects: crate::policy::immix::DEFRAG || crate::policy::immix::PREFER_COPY_ON_NURSERY_GC,
// If we disable moving in Immix, this is a non-moving plan.
moves_objects: !cfg!(feature = "immix_non_moving"),
needs_log_bit: true,
barrier: crate::plan::BarrierSelector::ObjectBarrier,
// 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.
Expand Down Expand Up @@ -164,7 +164,7 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {

fn current_gc_may_move_object(&self) -> bool {
if self.is_current_gc_nursery() {
PREFER_COPY_ON_NURSERY_GC
self.get_immix_space().prefer_copy_on_nursery_gc()
} else {
self.get_immix_space().in_defrag()
}
Expand Down Expand Up @@ -263,7 +263,7 @@ impl<VM: VMBinding> crate::plan::generational::global::GenerationalPlanExt<VM> f
self.immix
.immix_space
.trace_object_without_moving(queue, object)
} else if crate::policy::immix::PREFER_COPY_ON_NURSERY_GC {
} else if self.immix.immix_space.prefer_copy_on_nursery_gc() {
let ret = self.immix.immix_space.trace_object_with_opportunistic_copy(
queue,
object,
Expand Down Expand Up @@ -330,6 +330,7 @@ impl<VM: VMBinding> StickyImmix<VM> {
// In StickyImmix, both young and old objects are allocated in the ImmixSpace.
#[cfg(feature = "vo_bit")]
mixed_age: true,
never_move_objects: false,
},
);
Self {
Expand Down
9 changes: 4 additions & 5 deletions src/policy/immix/defrag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,18 @@ impl Defrag {
}

/// Determine whether the current GC should do defragmentation.
#[allow(clippy::too_many_arguments)]
pub fn decide_whether_to_defrag(
&self,
defrag_enabled: bool,
emergency_collection: bool,
collect_whole_heap: bool,
collection_attempts: usize,
user_triggered: bool,
exhausted_reusable_space: bool,
full_heap_system_gc: bool,
) {
let in_defrag = super::DEFRAG
let in_defrag = defrag_enabled
&& (emergency_collection
|| (collection_attempts > 1)
|| !exhausted_reusable_space
Expand Down Expand Up @@ -116,9 +118,8 @@ impl Defrag {
}

/// Prepare work. Should be called in ImmixSpace::prepare.
#[allow(clippy::assertions_on_constants)]
pub fn prepare<VM: VMBinding>(&self, space: &ImmixSpace<VM>, plan_stats: StatsForDefrag) {
debug_assert!(super::DEFRAG);
debug_assert!(space.is_defrag_enabled());
self.defrag_space_exhausted.store(false, Ordering::Release);

// Calculate available free space for defragmentation.
Expand Down Expand Up @@ -207,9 +208,7 @@ impl Defrag {
}

/// Reset the in-defrag state.
#[allow(clippy::assertions_on_constants)]
pub fn reset_in_defrag(&self) {
debug_assert!(super::DEFRAG);
self.in_defrag_collection.store(false, Ordering::Release);
}
}
63 changes: 46 additions & 17 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub struct ImmixSpaceArgs {
// Currently only used when "vo_bit" is enabled. Using #[cfg(...)] to eliminate dead code warning.
#[cfg(feature = "vo_bit")]
pub mixed_age: bool,
/// Disable copying for this Immix space.
pub never_move_objects: bool,
}

unsafe impl<VM: VMBinding> Sync for ImmixSpace<VM> {}
Expand All @@ -84,7 +86,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {

fn get_forwarded_object(&self, object: ObjectReference) -> Option<ObjectReference> {
// If we never move objects, look no further.
if super::NEVER_MOVE_OBJECTS {
if !self.is_movable() {
return None;
}

Expand All @@ -102,7 +104,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
}

// If we never move objects, look no further.
if super::NEVER_MOVE_OBJECTS {
if !self.is_movable() {
return false;
}

Expand All @@ -122,7 +124,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.is_object_pinned::<VM>(object)
}
fn is_movable(&self) -> bool {
!super::NEVER_MOVE_OBJECTS
!self.space_args.never_move_objects
}

#[cfg(feature = "sanity")]
Expand Down Expand Up @@ -276,23 +278,37 @@ impl<VM: VMBinding> ImmixSpace<VM> {

pub fn new(
args: crate::policy::space::PlanCreateSpaceArgs<VM>,
space_args: ImmixSpaceArgs,
mut space_args: ImmixSpaceArgs,
) -> Self {
#[cfg(feature = "immix_non_moving")]
info!(
"Creating non-moving ImmixSpace: {}. Block size: 2^{}",
args.name,
Block::LOG_BYTES
);

if space_args.unlog_object_when_traced {
assert!(
args.constraints.needs_log_bit,
"Invalid args when the plan does not use log bit"
);
}

super::validate_features();
// Make sure we override the space args if we force non moving Immix
if cfg!(feature = "immix_non_moving") && !space_args.never_move_objects {
info!(
"Overriding never_moves_objects for Immix Space {}, as the immix_non_moving feature is set. Block size: 2^{}",
args.name,
Block::LOG_BYTES,
);
space_args.never_move_objects = true;
}

// validate features
if super::BLOCK_ONLY {
assert!(
space_args.never_move_objects,
"Block-only immix must not move objects"
);
}
assert!(
Block::LINES / 2 <= u8::MAX as usize - 2,
"Number of lines in a block should not exceed BlockState::MARK_MARKED"
);

#[cfg(feature = "vo_bit")]
vo_bit::helper::validate_config::<VM>();
let vm_map = args.vm_map;
Expand Down Expand Up @@ -356,6 +372,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
full_heap_system_gc: bool,
) -> bool {
self.defrag.decide_whether_to_defrag(
self.is_defrag_enabled(),
emergency_collection,
collect_whole_heap,
collection_attempts,
Expand Down Expand Up @@ -390,7 +407,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
}

// Prepare defrag info
if super::DEFRAG {
if self.is_defrag_enabled() {
self.defrag.prepare(self, plan_stats);
}

Expand Down Expand Up @@ -483,7 +500,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
/// Return whether this GC was a defrag GC, as a plan may want to know this.
pub fn end_of_gc(&mut self) -> bool {
let did_defrag = self.defrag.in_defrag();
if super::DEFRAG {
if self.is_defrag_enabled() {
self.defrag.reset_in_defrag();
}
did_defrag
Expand Down Expand Up @@ -806,8 +823,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
Some((start, end))
}

pub fn is_last_gc_exhaustive(did_defrag_for_last_gc: bool) -> bool {
if super::DEFRAG {
pub fn is_last_gc_exhaustive(&self, did_defrag_for_last_gc: bool) -> bool {
if self.is_defrag_enabled() {
did_defrag_for_last_gc
} else {
// If defrag is disabled, every GC is exhaustive.
Expand All @@ -833,6 +850,18 @@ impl<VM: VMBinding> ImmixSpace<VM> {
self.mark_lines(object);
}
}

pub(crate) fn prefer_copy_on_nursery_gc(&self) -> bool {
self.is_nursery_copy_enabled()
}

pub(crate) fn is_nursery_copy_enabled(&self) -> bool {
!self.space_args.never_move_objects && !cfg!(feature = "sticky_immix_non_moving_nursery")
}

pub(crate) fn is_defrag_enabled(&self) -> bool {
!self.space_args.never_move_objects
}
}

/// A work packet to prepare each block for a major GC.
Expand Down Expand Up @@ -867,7 +896,7 @@ impl<VM: VMBinding> GCWork<VM> for PrepareBlockState<VM> {
continue;
}
// Check if this block needs to be defragmented.
let is_defrag_source = if !super::DEFRAG {
let is_defrag_source = if !self.space.is_defrag_enabled() {
// Do not set any block as defrag source if defrag is disabled.
false
} else if super::DEFRAG_EVERY_BLOCK {
Expand Down
25 changes: 0 additions & 25 deletions src/policy/immix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ pub const MAX_IMMIX_OBJECT_SIZE: usize = Block::BYTES >> 1;
/// Mark/sweep memory for block-level only
pub const BLOCK_ONLY: bool = false;

/// Do we allow Immix to do defragmentation?
pub const DEFRAG: bool = !cfg!(feature = "immix_non_moving"); // defrag if we are allowed to move.

// STRESS COPYING: Set the feature 'immix_stress_copying' so that Immix will copy as many objects as possible.
// Useful for debugging copying GC if you cannot use SemiSpace.
//
Expand Down Expand Up @@ -46,28 +43,6 @@ pub const DEFRAG_HEADROOM_PERCENT: usize = if cfg!(feature = "immix_stress_copyi
2
};

/// If Immix is used as a nursery space, do we prefer copy?
pub const PREFER_COPY_ON_NURSERY_GC: bool =
!cfg!(feature = "immix_non_moving") && !cfg!(feature = "sticky_immix_non_moving_nursery"); // copy nursery objects if we are allowed to move.

/// In some cases/settings, Immix may never move objects.
/// Currently we only have two cases where we move objects: 1. defrag, 2. nursery copy.
/// If we do neither, we will not move objects.
/// If we have other reasons to move objects, we need to add them here.
pub const NEVER_MOVE_OBJECTS: bool = !DEFRAG && !PREFER_COPY_ON_NURSERY_GC;

/// Mark lines when scanning objects.
/// Otherwise, do it at mark time.
pub const MARK_LINE_AT_SCAN_TIME: bool = true;

macro_rules! validate {
($x: expr) => { assert!($x, stringify!($x)) };
($x: expr => $y: expr) => { if $x { assert!($y, stringify!($x implies $y)) } };
}

fn validate_features() {
// Block-only immix cannot do defragmentation
validate!(DEFRAG => !BLOCK_ONLY);
// Number of lines in a block should not exceed BlockState::MARK_MARKED
assert!(Block::LINES / 2 <= u8::MAX as usize - 2);
}