Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ malloc_counted_size = []
# Count the size of all live objects in GC
count_live_bytes_in_gc = []

# Count the size of live objects in immixspace (the ones that can actually be opportunistically moved)
count_live_bytes_immixspace = []

# Workaround a problem where bpftrace scripts (see tools/tracing/timeline/capture.bt) cannot
# capture the type names of work packets.
bpftrace_workaround = []
Expand Down
36 changes: 36 additions & 0 deletions src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ pub struct GlobalState {
/// This stores the size in bytes for all the live objects in last GC. This counter is only updated in the GC release phase.
#[cfg(feature = "count_live_bytes_in_gc")]
pub(crate) live_bytes_in_last_gc: AtomicUsize,
/// These keep track of the fragmentation rate in immixspace
#[cfg(feature = "count_live_bytes_immixspace")]
pub(crate) live_bytes_in_immixspace: AtomicUsize,
#[cfg(feature = "count_live_bytes_immixspace")]
pub(crate) fragmentation_rate_in_immixspace: AtomicUsize,
}

impl GlobalState {
Expand Down Expand Up @@ -188,6 +193,33 @@ impl GlobalState {
pub fn set_live_bytes_in_last_gc(&self, size: usize) {
self.live_bytes_in_last_gc.store(size, Ordering::SeqCst);
}

#[cfg(feature = "count_live_bytes_immixspace")]
pub fn set_live_bytes_in_immixspace(&self, size: usize) {
self.live_bytes_in_immixspace.store(size, Ordering::SeqCst);
}

#[cfg(feature = "count_live_bytes_immixspace")]
pub fn get_live_bytes_in_immixspace(&self) -> usize {
self.live_bytes_in_immixspace.load(Ordering::SeqCst)
}

#[cfg(feature = "count_live_bytes_immixspace")]
pub fn increase_live_bytes_in_immixspace_by(&self, size: usize) {
self.live_bytes_in_immixspace
.fetch_add(size, Ordering::SeqCst);
}

#[cfg(feature = "count_live_bytes_immixspace")]
pub fn get_fragmentation_rate_in_immixspace(&self) -> usize {
self.fragmentation_rate_in_immixspace.load(Ordering::SeqCst)
}

#[cfg(feature = "count_live_bytes_immixspace")]
pub fn set_fragmentation_rate_in_immixspace(&self, size: usize) {
self.fragmentation_rate_in_immixspace
.store(size, Ordering::SeqCst);
}
}

impl Default for GlobalState {
Expand All @@ -209,6 +241,10 @@ impl Default for GlobalState {
malloc_bytes: AtomicUsize::new(0),
#[cfg(feature = "count_live_bytes_in_gc")]
live_bytes_in_last_gc: AtomicUsize::new(0),
#[cfg(feature = "count_live_bytes_immixspace")]
live_bytes_in_immixspace: AtomicUsize::new(0),
#[cfg(feature = "count_live_bytes_immixspace")]
fragmentation_rate_in_immixspace: AtomicUsize::new(0),
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,19 @@ pub fn live_bytes_in_last_gc<VM: VMBinding>(mmtk: &MMTK<VM>) -> usize {
mmtk.state.live_bytes_in_last_gc.load(Ordering::SeqCst)
}

/// Return the percentage of fragmentation of the immixspace (e.g. 42.98 percent). To do that we count the size of every live object
/// in the immixspace. Since MMTk accounts for memory in pages, we return the ratio between this number and
/// the number of used bytes (according to the used pages by the immixspace).
/// The value returned by this method is only updated when we finish tracing in a GC. A recommended timing
/// to call this method is at the end of a GC (e.g. when the runtime is about to resume threads).
#[cfg(feature = "count_live_bytes_immixspace")]
pub fn fragmentation_rate_in_immixspace<VM: VMBinding>(mmtk: &MMTK<VM>) -> f64 {
mmtk.state
.fragmentation_rate_in_immixspace
.load(Ordering::SeqCst) as f64
/ 100.0
}

/// Return the starting address of the heap. *Note that currently MMTk uses
/// a fixed address range as heap.*
pub fn starting_heap_address() -> Address {
Expand Down
32 changes: 32 additions & 0 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for ImmixSpace
debug_assert!(self.in_space(object));
self.mark_lines(object);
}

// count the bytes for each object in immixspace to
// check for fragmentation
#[cfg(feature = "count_live_bytes_immixspace")]
self.common
.global_state
.increase_live_bytes_in_immixspace_by(VM::VMObjectModel::get_current_size(object));
}

fn may_move_objects<const KIND: TraceKind>() -> bool {
Expand Down Expand Up @@ -464,6 +471,31 @@ impl<VM: VMBinding> ImmixSpace<VM> {

self.lines_consumed.store(0, Ordering::Relaxed);

// calculate the fragmentation rate
#[cfg(feature = "count_live_bytes_immixspace")]
{
trace!(
"Live bytes in immixspace = {}",
self.common.global_state.get_live_bytes_in_immixspace()
);
trace!("Reserved pages in immixspace = {}", self.reserved_pages());

trace!(
"Reserved bytes in immixspace = {}",
self.reserved_pages() << LOG_BYTES_IN_PAGE
);
let f_rate: f64 = self.common.global_state.get_live_bytes_in_immixspace() as f64
/ (self.reserved_pages() << LOG_BYTES_IN_PAGE) as f64;

let f_rate_usize: usize = (f_rate * 10000.0) as usize;

debug_assert!((0.0..=1.0).contains(&f_rate));

self.common
.global_state
.set_fragmentation_rate_in_immixspace(f_rate_usize);
}

did_defrag
}

Expand Down