From c2a79ad2ea40c0a3d25ad981fa63ba8c79286ecb Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Thu, 29 Feb 2024 04:13:55 +0000 Subject: [PATCH 01/13] Adding feature to query the fragmentation of immixspace --- Cargo.toml | 3 +++ src/global_state.rs | 41 ++++++++++++++++++++++++++++++++++ src/memory_manager.rs | 13 +++++++++++ src/policy/immix/immixspace.rs | 32 ++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e1f108df3f..c3bcc4ab27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 = [] diff --git a/src/global_state.rs b/src/global_state.rs index 4823119851..86a2212b5e 100644 --- a/src/global_state.rs +++ b/src/global_state.rs @@ -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 { @@ -188,6 +193,38 @@ 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 get_live_bytes_in_last_gc(&self) -> usize { + self.live_bytes_in_immixspace.load(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 { @@ -209,6 +246,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), } } } diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 77d1b29d30..fa6f42d302 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -557,6 +557,19 @@ pub fn live_bytes_in_last_gc(mmtk: &MMTK) -> 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(mmtk: &MMTK) -> 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 { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index bf72394c01..150ca5f921 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -217,6 +217,13 @@ impl crate::policy::gc_work::PolicyTraceObject 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() -> bool { @@ -464,6 +471,31 @@ impl ImmixSpace { 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!(f_rate <= 1.0 && f_rate >= 0.0); + + self.common + .global_state + .set_fragmentation_rate_in_immixspace(f_rate_usize); + } + did_defrag } From f081e4ffb3cc1e6493a339b87f797085fcd3ac54 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Thu, 29 Feb 2024 04:23:19 +0000 Subject: [PATCH 02/13] Removing duplicated method --- src/global_state.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/global_state.rs b/src/global_state.rs index 86a2212b5e..7066be52b6 100644 --- a/src/global_state.rs +++ b/src/global_state.rs @@ -194,11 +194,6 @@ impl GlobalState { self.live_bytes_in_last_gc.store(size, Ordering::SeqCst); } - #[cfg(feature = "count_live_bytes_immixspace")] - pub fn get_live_bytes_in_last_gc(&self) -> usize { - self.live_bytes_in_immixspace.load(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); From 530051b966c02f8839b3ade3f2c08b1a3df09011 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Thu, 29 Feb 2024 04:33:27 +0000 Subject: [PATCH 03/13] Refactor range check --- src/policy/immix/immixspace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 150ca5f921..981865bfff 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -489,7 +489,7 @@ impl ImmixSpace { let f_rate_usize: usize = (f_rate * 10000.0) as usize; - debug_assert!(f_rate <= 1.0 && f_rate >= 0.0); + debug_assert!((0.0..=1.0).contains(&f_rate)); self.common .global_state From ee21a9c68e04730489a8f3509d7a2421e8047c0a Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Thu, 29 Feb 2024 22:59:56 +0000 Subject: [PATCH 04/13] Moving stats from global state to immixspace --- src/global_state.rs | 36 -------------------------- src/memory_manager.rs | 19 +++++++++++--- src/policy/immix/immixspace.rs | 47 +++++++++++++++++++++++++--------- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/global_state.rs b/src/global_state.rs index 7066be52b6..4823119851 100644 --- a/src/global_state.rs +++ b/src/global_state.rs @@ -43,11 +43,6 @@ 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 { @@ -193,33 +188,6 @@ 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 { @@ -241,10 +209,6 @@ 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), } } } diff --git a/src/memory_manager.rs b/src/memory_manager.rs index fa6f42d302..d42d9be352 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -564,10 +564,21 @@ pub fn live_bytes_in_last_gc(mmtk: &MMTK) -> usize { /// 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(mmtk: &MMTK) -> f64 { - mmtk.state - .fragmentation_rate_in_immixspace - .load(Ordering::SeqCst) as f64 - / 100.0 + use crate::policy::immix::ImmixSpace; + use crate::policy::space::Space; + let mut rate = None; + mmtk.get_plan() + .for_each_space(&mut |space: &dyn Space| { + if let Some(immix) = space.downcast_ref::>() { + assert!( + rate.is_none(), + "There are multiple Immix spaces in the plan." + ); + // Get the stats here from ImmixSpace + rate = Some(immix.get_fragmentation_rate()); + } + }); + rate.expect("No Immix space in the plan.") as f64 / 100.0 } /// Return the starting address of the heap. *Note that currently MMTk uses diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 981865bfff..b7b56bbfca 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -54,6 +54,11 @@ pub struct ImmixSpace { scheduler: Arc>, /// Some settings for this space space_args: ImmixSpaceArgs, + /// Keeping track of fragmentation rate + #[cfg(feature = "count_live_bytes_immixspace")] + live_bytes_in_immixspace: AtomicUsize, + #[cfg(feature = "count_live_bytes_immixspace")] + fragmentation_rate: AtomicUsize, } /// Some arguments for Immix Space. @@ -221,9 +226,7 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace // 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)); + self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); } fn may_move_objects() -> bool { @@ -322,6 +325,10 @@ impl ImmixSpace { mark_state: Self::MARKED_STATE, scheduler: scheduler.clone(), space_args, + #[cfg(feature = "count_live_bytes_immixspace")] + live_bytes_in_immixspace: AtomicUsize::new(0), + #[cfg(feature = "count_live_bytes_immixspace")] + fragmentation_rate: AtomicUsize::new(0), } } @@ -474,26 +481,21 @@ impl ImmixSpace { // 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!("Live bytes in immixspace = {}", self.get_live_bytes()); 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: f64 = + self.get_live_bytes() 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); + self.set_fragmentation_rate(f_rate_usize); } did_defrag @@ -839,6 +841,27 @@ impl ImmixSpace { self.mark_lines(object); } } + + #[cfg(feature = "count_live_bytes_immixspace")] + pub fn get_live_bytes(&self) -> usize { + self.live_bytes_in_immixspace.load(Ordering::SeqCst) + } + + #[cfg(feature = "count_live_bytes_immixspace")] + pub fn increase_live_bytes(&self, size: usize) { + self.live_bytes_in_immixspace + .fetch_add(size, Ordering::SeqCst); + } + + #[cfg(feature = "count_live_bytes_immixspace")] + pub fn get_fragmentation_rate(&self) -> usize { + self.fragmentation_rate.load(Ordering::SeqCst) + } + + #[cfg(feature = "count_live_bytes_immixspace")] + pub fn set_fragmentation_rate(&self, size: usize) { + self.fragmentation_rate.store(size, Ordering::SeqCst); + } } /// A work packet to prepare each block for a major GC. From bd3305f502c2c78e821c8b71859291c50f6e98df Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Mon, 4 Mar 2024 03:26:42 +0000 Subject: [PATCH 05/13] Zeroing the live bytes right before GC starts --- src/policy/immix/immixspace.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index b7b56bbfca..48eec4c0f2 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -450,6 +450,9 @@ impl ImmixSpace { self.scheduler.work_buckets[WorkBucketStage::ClearVOBits].bulk_add(work_packets); } } + + #[cfg(feature = "count_live_bytes_immixspace")] + self.set_live_bytes(0); } /// Release for the immix space. This is called when a GC finished. @@ -847,6 +850,11 @@ impl ImmixSpace { self.live_bytes_in_immixspace.load(Ordering::SeqCst) } + #[cfg(feature = "count_live_bytes_immixspace")] + pub fn set_live_bytes(&self, size: usize) { + self.live_bytes_in_immixspace.store(size, Ordering::SeqCst) + } + #[cfg(feature = "count_live_bytes_immixspace")] pub fn increase_live_bytes(&self, size: usize) { self.live_bytes_in_immixspace From 1f39198ac20c98228e95256b0f82b8b34465bec6 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Mon, 4 Mar 2024 04:12:19 +0000 Subject: [PATCH 06/13] Trying to count live blocks and live lines --- Cargo.toml | 1 + src/memory_manager.rs | 6 +-- src/policy/immix/immixspace.rs | 81 +++++++++++++++++++++++++--------- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3bcc4ab27..1245a5490b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ static_assertions = "1.1.0" strum = "0.25" strum_macros = "0.25" sysinfo = "0.29" +chrono = "*" [dev-dependencies] paste = "1.0.8" diff --git a/src/memory_manager.rs b/src/memory_manager.rs index d42d9be352..76fc454ead 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -557,13 +557,13 @@ pub fn live_bytes_in_last_gc(mmtk: &MMTK) -> 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 +/// Return the percentage of occupation 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(mmtk: &MMTK) -> f64 { +pub fn occupation_rate_in_immixspace(mmtk: &MMTK) -> f64 { use crate::policy::immix::ImmixSpace; use crate::policy::space::Space; let mut rate = None; @@ -575,7 +575,7 @@ pub fn fragmentation_rate_in_immixspace(mmtk: &MMTK) -> f64 { "There are multiple Immix spaces in the plan." ); // Get the stats here from ImmixSpace - rate = Some(immix.get_fragmentation_rate()); + rate = Some(immix.get_occupation_rate()); } }); rate.expect("No Immix space in the plan.") as f64 / 100.0 diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 48eec4c0f2..5e26dbb232 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -58,7 +58,7 @@ pub struct ImmixSpace { #[cfg(feature = "count_live_bytes_immixspace")] live_bytes_in_immixspace: AtomicUsize, #[cfg(feature = "count_live_bytes_immixspace")] - fragmentation_rate: AtomicUsize, + occupation_rate: AtomicUsize, } /// Some arguments for Immix Space. @@ -328,7 +328,7 @@ impl ImmixSpace { #[cfg(feature = "count_live_bytes_immixspace")] live_bytes_in_immixspace: AtomicUsize::new(0), #[cfg(feature = "count_live_bytes_immixspace")] - fragmentation_rate: AtomicUsize::new(0), + occupation_rate: AtomicUsize::new(0), } } @@ -483,25 +483,66 @@ impl ImmixSpace { // calculate the fragmentation rate #[cfg(feature = "count_live_bytes_immixspace")] - { - trace!("Live bytes in immixspace = {}", self.get_live_bytes()); - 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.get_live_bytes() as f64 / (self.reserved_pages() << LOG_BYTES_IN_PAGE) as f64; + self.dump_memory_stats(); - let f_rate_usize: usize = (f_rate * 10000.0) as usize; + did_defrag + } - debug_assert!((0.0..=1.0).contains(&f_rate)); + fn dump_memory_stats(&mut self) { + #[derive(Default)] + struct Dist { + live_blocks: usize, + live_lines: usize, + } + let mut dist = Dist::default(); + for chunk in self.chunk_map.all_chunks() { + if !self.address_in_space(chunk.start()) { + continue; + } - self.set_fragmentation_rate(f_rate_usize); + for block in chunk + .iter_region::() + .filter(|b| b.get_state() != BlockState::Unallocated) + { + dist.live_blocks += 1; + for _line in block + .lines() + .filter(|l| l.is_marked(self.line_mark_state.load(Ordering::Acquire))) + { + dist.live_lines = 1; + } + } } - did_defrag + println!( + "{} immixspace", + chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S") + ); + println!("Live bytes = {}", self.get_live_bytes()); + println!("Reserved pages = {}", self.reserved_pages()); + println!( + "Reserved pages (bytes) = {}", + self.reserved_pages() << LOG_BYTES_IN_PAGE + ); + println!("Live blocks = {}", dist.live_blocks); + println!( + "Live blocks (bytes) = {}", + dist.live_blocks << Block::LOG_BYTES + ); + println!("Live lines = {}", dist.live_lines); + println!( + "Live lines (bytes) = {}", + dist.live_lines << Line::LOG_BYTES + ); + + let o_rate: f64 = + self.get_live_bytes() as f64 / (self.reserved_pages() << LOG_BYTES_IN_PAGE) as f64; + + let o_rate_usize: usize = (o_rate * 10000.0) as usize; + + debug_assert!((0.0..=1.0).contains(&o_rate)); + + self.set_occupation_rate(o_rate_usize); } /// Generate chunk sweep tasks @@ -862,13 +903,13 @@ impl ImmixSpace { } #[cfg(feature = "count_live_bytes_immixspace")] - pub fn get_fragmentation_rate(&self) -> usize { - self.fragmentation_rate.load(Ordering::SeqCst) + pub fn get_occupation_rate(&self) -> usize { + self.occupation_rate.load(Ordering::SeqCst) } #[cfg(feature = "count_live_bytes_immixspace")] - pub fn set_fragmentation_rate(&self, size: usize) { - self.fragmentation_rate.store(size, Ordering::SeqCst); + pub fn set_occupation_rate(&self, size: usize) { + self.occupation_rate.store(size, Ordering::SeqCst); } } From 06284e1e19af239a2cf1e9c6b48acedb9501f561 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Mon, 4 Mar 2024 09:37:45 +0000 Subject: [PATCH 07/13] Adding feature to dump memory stats (from los and immixspace) --- Cargo.toml | 4 +- src/memory_manager.rs | 24 --------- src/plan/global.rs | 4 ++ src/plan/immix/global.rs | 6 +++ src/policy/immix/immixspace.rs | 91 +++++++++++++--------------------- src/policy/largeobjectspace.rs | 45 +++++++++++++++++ src/scheduler/gc_work.rs | 3 ++ 7 files changed, 95 insertions(+), 82 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1245a5490b..3840260908 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,8 +153,8 @@ 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 = [] +# Dump memory stats about the plan (live bytes, live lines, live blocks, and used pages) +dump_memory_stats = [] # Workaround a problem where bpftrace scripts (see tools/tracing/timeline/capture.bt) cannot # capture the type names of work packets. diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 76fc454ead..77d1b29d30 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -557,30 +557,6 @@ pub fn live_bytes_in_last_gc(mmtk: &MMTK) -> usize { mmtk.state.live_bytes_in_last_gc.load(Ordering::SeqCst) } -/// Return the percentage of occupation 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 occupation_rate_in_immixspace(mmtk: &MMTK) -> f64 { - use crate::policy::immix::ImmixSpace; - use crate::policy::space::Space; - let mut rate = None; - mmtk.get_plan() - .for_each_space(&mut |space: &dyn Space| { - if let Some(immix) = space.downcast_ref::>() { - assert!( - rate.is_none(), - "There are multiple Immix spaces in the plan." - ); - // Get the stats here from ImmixSpace - rate = Some(immix.get_occupation_rate()); - } - }); - rate.expect("No Immix space in the plan.") 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 { diff --git a/src/plan/global.rs b/src/plan/global.rs index 5b28c4b720..eb8dd8ffbe 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -315,6 +315,10 @@ pub trait Plan: 'static + HasSpaces + Sync + Downcast { space.verify_side_metadata_sanity(&mut side_metadata_sanity_checker); }) } + + // Dump memory stats for the plan + #[cfg(feature = "dump_memory_stats")] + fn dump_memory_stats(&self) {} } impl_downcast!(Plan assoc VM); diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 0741ba3a99..571e1d31c7 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -116,6 +116,12 @@ impl Plan for Immix { fn common(&self) -> &CommonPlan { &self.common } + + #[cfg(feature = "dump_memory_stats")] + fn dump_memory_stats(&self) { + self.immix_space.dump_memory_stats(); + self.common.los.dump_memory_stats(); + } } impl Immix { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 5e26dbb232..64ca931919 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -54,11 +54,9 @@ pub struct ImmixSpace { scheduler: Arc>, /// Some settings for this space space_args: ImmixSpaceArgs, - /// Keeping track of fragmentation rate - #[cfg(feature = "count_live_bytes_immixspace")] - live_bytes_in_immixspace: AtomicUsize, - #[cfg(feature = "count_live_bytes_immixspace")] - occupation_rate: AtomicUsize, + /// Keeping track of live bytes + #[cfg(feature = "dump_memory_stats")] + live_bytes: AtomicUsize, } /// Some arguments for Immix Space. @@ -223,9 +221,8 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace self.mark_lines(object); } - // count the bytes for each object in immixspace to - // check for fragmentation - #[cfg(feature = "count_live_bytes_immixspace")] + // count the bytes for each object in immixspace + #[cfg(feature = "dump_memory_stats")] self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); } @@ -325,10 +322,8 @@ impl ImmixSpace { mark_state: Self::MARKED_STATE, scheduler: scheduler.clone(), space_args, - #[cfg(feature = "count_live_bytes_immixspace")] - live_bytes_in_immixspace: AtomicUsize::new(0), - #[cfg(feature = "count_live_bytes_immixspace")] - occupation_rate: AtomicUsize::new(0), + #[cfg(feature = "dump_memory_stats")] + live_bytes: AtomicUsize::new(0), } } @@ -451,7 +446,7 @@ impl ImmixSpace { } } - #[cfg(feature = "count_live_bytes_immixspace")] + #[cfg(feature = "dump_memory_stats")] self.set_live_bytes(0); } @@ -481,14 +476,11 @@ impl ImmixSpace { self.lines_consumed.store(0, Ordering::Relaxed); - // calculate the fragmentation rate - #[cfg(feature = "count_live_bytes_immixspace")] - self.dump_memory_stats(); - did_defrag } - fn dump_memory_stats(&mut self) { + #[cfg(feature = "dump_memory_stats")] + pub(crate) fn dump_memory_stats(&self) { #[derive(Default)] struct Dist { live_blocks: usize, @@ -505,11 +497,18 @@ impl ImmixSpace { .filter(|b| b.get_state() != BlockState::Unallocated) { dist.live_blocks += 1; - for _line in block - .lines() - .filter(|l| l.is_marked(self.line_mark_state.load(Ordering::Acquire))) - { - dist.live_lines = 1; + match block.get_state() { + BlockState::Marked => { + panic!("At this point the block should have been swept already"); + } + BlockState::Unmarked => { + // Block is unmarked and cannot be reused (has no holes) + dist.live_lines += Block::LINES; + } + BlockState::Reusable { unavailable_lines } => { + dist.live_lines += unavailable_lines as usize; + } + BlockState::Unallocated => {} } } } @@ -518,31 +517,22 @@ impl ImmixSpace { "{} immixspace", chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S") ); - println!("Live bytes = {}", self.get_live_bytes()); - println!("Reserved pages = {}", self.reserved_pages()); + println!("\tLive bytes = {}", self.get_live_bytes()); + println!("\tReserved pages = {}", self.reserved_pages()); println!( - "Reserved pages (bytes) = {}", + "\tReserved pages (bytes) = {}", self.reserved_pages() << LOG_BYTES_IN_PAGE ); - println!("Live blocks = {}", dist.live_blocks); + println!("\tLive blocks = {}", dist.live_blocks); println!( - "Live blocks (bytes) = {}", + "\tLive blocks (bytes) = {}", dist.live_blocks << Block::LOG_BYTES ); - println!("Live lines = {}", dist.live_lines); + println!("\tLive lines = {}", dist.live_lines); println!( - "Live lines (bytes) = {}", + "\tLive lines (bytes) = {}", dist.live_lines << Line::LOG_BYTES ); - - let o_rate: f64 = - self.get_live_bytes() as f64 / (self.reserved_pages() << LOG_BYTES_IN_PAGE) as f64; - - let o_rate_usize: usize = (o_rate * 10000.0) as usize; - - debug_assert!((0.0..=1.0).contains(&o_rate)); - - self.set_occupation_rate(o_rate_usize); } /// Generate chunk sweep tasks @@ -886,30 +876,19 @@ impl ImmixSpace { } } - #[cfg(feature = "count_live_bytes_immixspace")] + #[cfg(feature = "dump_memory_stats")] pub fn get_live_bytes(&self) -> usize { - self.live_bytes_in_immixspace.load(Ordering::SeqCst) + self.live_bytes.load(Ordering::SeqCst) } - #[cfg(feature = "count_live_bytes_immixspace")] + #[cfg(feature = "dump_memory_stats")] pub fn set_live_bytes(&self, size: usize) { - self.live_bytes_in_immixspace.store(size, Ordering::SeqCst) + self.live_bytes.store(size, Ordering::SeqCst) } - #[cfg(feature = "count_live_bytes_immixspace")] + #[cfg(feature = "dump_memory_stats")] pub fn increase_live_bytes(&self, size: usize) { - self.live_bytes_in_immixspace - .fetch_add(size, Ordering::SeqCst); - } - - #[cfg(feature = "count_live_bytes_immixspace")] - pub fn get_occupation_rate(&self) -> usize { - self.occupation_rate.load(Ordering::SeqCst) - } - - #[cfg(feature = "count_live_bytes_immixspace")] - pub fn set_occupation_rate(&self, size: usize) { - self.occupation_rate.store(size, Ordering::SeqCst); + self.live_bytes.fetch_add(size, Ordering::SeqCst); } } diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 64a13c8f37..d9c027de27 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -6,6 +6,8 @@ use crate::policy::sft::GCWorkerMutRef; use crate::policy::sft::SFT; use crate::policy::space::{CommonSpace, Space}; use crate::util::constants::BYTES_IN_PAGE; +#[cfg(feature = "dump_memory_stats")] +use crate::util::constants::LOG_BYTES_IN_PAGE; use crate::util::heap::{FreeListPageResource, PageResource}; use crate::util::metadata; use crate::util::opaque_pointer::*; @@ -13,6 +15,8 @@ use crate::util::treadmill::TreadMill; use crate::util::{Address, ObjectReference}; use crate::vm::ObjectModel; use crate::vm::VMBinding; +#[cfg(feature = "dump_memory_stats")] +use std::sync::atomic::AtomicUsize; #[allow(unused)] const PAGE_MASK: usize = !(BYTES_IN_PAGE - 1); @@ -28,6 +32,9 @@ pub struct LargeObjectSpace { mark_state: u8, in_nursery_gc: bool, treadmill: TreadMill, + /// Keeping track of live bytes + #[cfg(feature = "dump_memory_stats")] + live_bytes: AtomicUsize, } impl SFT for LargeObjectSpace { @@ -136,6 +143,11 @@ impl crate::policy::gc_work::PolicyTraceObject for LargeObjec fn may_move_objects() -> bool { false } + + #[cfg(feature = "dump_memory_stats")] + fn post_scan_object(&self, object: ObjectReference) { + self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); + } } impl LargeObjectSpace { @@ -162,6 +174,8 @@ impl LargeObjectSpace { mark_state: 0, in_nursery_gc: false, treadmill: TreadMill::new(), + #[cfg(feature = "dump_memory_stats")] + live_bytes: AtomicUsize::new(0), } } @@ -172,6 +186,8 @@ impl LargeObjectSpace { } self.treadmill.flip(full_heap); self.in_nursery_gc = !full_heap; + #[cfg(feature = "dump_memory_stats")] + self.set_live_bytes(0); } pub fn release(&mut self, full_heap: bool) { @@ -303,6 +319,35 @@ impl LargeObjectSpace { ) & NURSERY_BIT == NURSERY_BIT } + + #[cfg(feature = "dump_memory_stats")] + pub fn get_live_bytes(&self) -> usize { + self.live_bytes.load(Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn set_live_bytes(&self, size: usize) { + self.live_bytes.store(size, Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn increase_live_bytes(&self, size: usize) { + self.live_bytes.fetch_add(size, Ordering::SeqCst); + } + + #[cfg(feature = "dump_memory_stats")] + pub(crate) fn dump_memory_stats(&self) { + println!( + "{} los", + chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S") + ); + println!("\tLive bytes = {}", self.get_live_bytes()); + println!("\tReserved pages = {}", self.reserved_pages()); + println!( + "\tReserved pages (bytes) = {}", + self.reserved_pages() << LOG_BYTES_IN_PAGE + ); + } } fn get_super_page(cell: Address) -> Address { diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 8cf4c74bee..3c51e96cb1 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -244,6 +244,9 @@ impl GCWork for EndOfGC { ); } + #[cfg(feature = "dump_memory_stats")] + mmtk.get_plan().dump_memory_stats(); + // We assume this is the only running work packet that accesses plan at the point of execution let plan_mut: &mut dyn Plan = unsafe { mmtk.get_plan_mut() }; plan_mut.end_of_gc(worker.tls); From 15474282a1084193bc60dea2bfb77fb377c03b84 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Mon, 4 Mar 2024 09:52:28 +0000 Subject: [PATCH 08/13] Print stats for sticky immix as well --- src/plan/sticky/immix/global.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plan/sticky/immix/global.rs b/src/plan/sticky/immix/global.rs index f913e2e4b8..c42d779d7a 100644 --- a/src/plan/sticky/immix/global.rs +++ b/src/plan/sticky/immix/global.rs @@ -80,6 +80,12 @@ impl Plan for StickyImmix { self.immix.common() } + #[cfg(feature = "dump_memory_stats")] + fn dump_memory_stats(&self) { + self.immix.immix_space.dump_memory_stats(); + self.common().los.dump_memory_stats(); + } + fn schedule_collection(&'static self, scheduler: &crate::scheduler::GCWorkScheduler) { let is_full_heap = self.requires_full_heap_collection(); self.gc_full_heap.store(is_full_heap, Ordering::SeqCst); From 184822c79d99dffb19b7fe612ae3e00aa9d848e0 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Tue, 5 Mar 2024 01:07:19 +0000 Subject: [PATCH 09/13] Removing dependency on chrono --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3840260908..7acbd92f04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ static_assertions = "1.1.0" strum = "0.25" strum_macros = "0.25" sysinfo = "0.29" -chrono = "*" [dev-dependencies] paste = "1.0.8" From f29494825797137b9a222168da8b07a7f06b3d99 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Tue, 5 Mar 2024 01:08:04 +0000 Subject: [PATCH 10/13] Adding assertion for live lines in immixspace; properly counting live bytes in los --- src/plan/global.rs | 2 +- src/policy/immix/immixspace.rs | 28 ++++++++++++++++++++++++---- src/policy/largeobjectspace.rs | 19 ++++++++++--------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/plan/global.rs b/src/plan/global.rs index eb8dd8ffbe..4e66a05d69 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -316,7 +316,7 @@ pub trait Plan: 'static + HasSpaces + Sync + Downcast { }) } - // Dump memory stats for the plan + /// Dump memory stats for the plan #[cfg(feature = "dump_memory_stats")] fn dump_memory_stats(&self) {} } diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 64ca931919..10d8de4d7b 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -481,12 +481,15 @@ impl ImmixSpace { #[cfg(feature = "dump_memory_stats")] pub(crate) fn dump_memory_stats(&self) { + use std::time::{SystemTime, UNIX_EPOCH}; + #[derive(Default)] struct Dist { live_blocks: usize, live_lines: usize, } let mut dist = Dist::default(); + for chunk in self.chunk_map.all_chunks() { if !self.address_in_space(chunk.start()) { continue; @@ -497,6 +500,17 @@ impl ImmixSpace { .filter(|b| b.get_state() != BlockState::Unallocated) { dist.live_blocks += 1; + + let line_mark_state = self.line_mark_state.load(Ordering::Acquire); + let mut live_lines_in_table = 0; + let mut live_lines_from_block_state = 0; + + for line in block.lines() { + if line.is_marked(line_mark_state) { + live_lines_in_table += 1; + } + } + match block.get_state() { BlockState::Marked => { panic!("At this point the block should have been swept already"); @@ -504,19 +518,25 @@ impl ImmixSpace { BlockState::Unmarked => { // Block is unmarked and cannot be reused (has no holes) dist.live_lines += Block::LINES; + live_lines_from_block_state += Block::LINES; } BlockState::Reusable { unavailable_lines } => { dist.live_lines += unavailable_lines as usize; + live_lines_from_block_state += unavailable_lines as usize; } BlockState::Unallocated => {} } + + assert_eq!(live_lines_in_table, live_lines_from_block_state); } } - println!( - "{} immixspace", - chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S") - ); + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + + println!("{:?} immixspace", since_the_epoch.as_millis()); println!("\tLive bytes = {}", self.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index d9c027de27..67a6d4b0d0 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -143,11 +143,6 @@ impl crate::policy::gc_work::PolicyTraceObject for LargeObjec fn may_move_objects() -> bool { false } - - #[cfg(feature = "dump_memory_stats")] - fn post_scan_object(&self, object: ObjectReference) { - self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); - } } impl LargeObjectSpace { @@ -298,6 +293,8 @@ impl LargeObjectSpace { break; } } + #[cfg(feature = "dump_memory_stats")] + self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); true } @@ -337,10 +334,14 @@ impl LargeObjectSpace { #[cfg(feature = "dump_memory_stats")] pub(crate) fn dump_memory_stats(&self) { - println!( - "{} los", - chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S") - ); + use std::time::{SystemTime, UNIX_EPOCH}; + + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + + println!("{} los", since_the_epoch.as_millis()); println!("\tLive bytes = {}", self.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( From 6f1c9246e55404f89c78eb56e3472c15cac94bc6 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Wed, 6 Mar 2024 23:02:10 +0000 Subject: [PATCH 11/13] Change printing info --- src/policy/immix/immixspace.rs | 2 +- src/policy/largeobjectspace.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 10d8de4d7b..c41281287b 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -536,7 +536,7 @@ impl ImmixSpace { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); - println!("{:?} immixspace", since_the_epoch.as_millis()); + println!("{:?} mmtk_immixspace", since_the_epoch.as_millis()); println!("\tLive bytes = {}", self.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 67a6d4b0d0..0c263cfb1d 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -341,7 +341,7 @@ impl LargeObjectSpace { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); - println!("{} los", since_the_epoch.as_millis()); + println!("{} mmtk_los", since_the_epoch.as_millis()); println!("\tLive bytes = {}", self.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( From aeb3aeb20157690c8f8563775363cad0d2c40447 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Wed, 6 Mar 2024 23:12:08 +0000 Subject: [PATCH 12/13] Adding statistics about number of objects scanned and objects moved in immixspace --- src/policy/immix/immixspace.rs | 55 +++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index c41281287b..e2d787a274 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -57,6 +57,11 @@ pub struct ImmixSpace { /// Keeping track of live bytes #[cfg(feature = "dump_memory_stats")] live_bytes: AtomicUsize, + /// Keeping track of the number of traced/copied objects + #[cfg(feature = "dump_memory_stats")] + live_objects: AtomicUsize, + #[cfg(feature = "dump_memory_stats")] + copied_objects: AtomicUsize, } /// Some arguments for Immix Space. @@ -221,9 +226,13 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace self.mark_lines(object); } - // count the bytes for each object in immixspace + // count the bytes for each object #[cfg(feature = "dump_memory_stats")] self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); + + // increase the number of objects scanned + #[cfg(feature = "dump_memory_stats")] + self.increase_live_objects(1); } fn may_move_objects() -> bool { @@ -324,6 +333,10 @@ impl ImmixSpace { space_args, #[cfg(feature = "dump_memory_stats")] live_bytes: AtomicUsize::new(0), + #[cfg(feature = "dump_memory_stats")] + live_objects: AtomicUsize::new(0), + #[cfg(feature = "dump_memory_stats")] + copied_objects: AtomicUsize::new(0), } } @@ -448,6 +461,10 @@ impl ImmixSpace { #[cfg(feature = "dump_memory_stats")] self.set_live_bytes(0); + #[cfg(feature = "dump_memory_stats")] + self.set_live_objects(0); + #[cfg(feature = "dump_memory_stats")] + self.set_copied_objects(0); } /// Release for the immix space. This is called when a GC finished. @@ -537,6 +554,8 @@ impl ImmixSpace { .expect("Time went backwards"); println!("{:?} mmtk_immixspace", since_the_epoch.as_millis()); + println!("\t#Live objects = {}", self.get_live_objects()); + println!("\t#Copied objects = {}", self.get_copied_objects()); println!("\tLive bytes = {}", self.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( @@ -727,6 +746,10 @@ impl ImmixSpace { let new_object = object_forwarding::forward_object::(object, semantics, copy_context); + // increase the number of objects being moved + #[cfg(feature = "dump_memory_stats")] + self.increase_copied_objects(1); + #[cfg(feature = "vo_bit")] vo_bit::helper::on_object_forwarded::(new_object); @@ -910,6 +933,36 @@ impl ImmixSpace { pub fn increase_live_bytes(&self, size: usize) { self.live_bytes.fetch_add(size, Ordering::SeqCst); } + + #[cfg(feature = "dump_memory_stats")] + pub fn get_live_objects(&self) -> usize { + self.live_objects.load(Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn set_live_objects(&self, size: usize) { + self.live_objects.store(size, Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn increase_live_objects(&self, size: usize) { + self.live_objects.fetch_add(size, Ordering::SeqCst); + } + + #[cfg(feature = "dump_memory_stats")] + pub fn get_copied_objects(&self) -> usize { + self.copied_objects.load(Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn set_copied_objects(&self, size: usize) { + self.copied_objects.store(size, Ordering::SeqCst) + } + + #[cfg(feature = "dump_memory_stats")] + pub fn increase_copied_objects(&self, size: usize) { + self.copied_objects.fetch_add(size, Ordering::SeqCst); + } } /// A work packet to prepare each block for a major GC. From 4cfac972af53772ae8ba5c839988635dc3f9b5b1 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Tue, 26 Mar 2024 23:27:59 +0000 Subject: [PATCH 13/13] Refactor code; turn on logs; set block size to 16K --- Cargo.toml | 2 +- src/plan/sticky/immix/global.rs | 2 +- src/policy/immix/block.rs | 4 +- src/policy/immix/immixspace.rs | 179 +++++++++++++++++++------------- src/policy/immix/mod.rs | 6 +- 5 files changed, 115 insertions(+), 78 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7acbd92f04..4669a0358c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ itertools = "0.12.0" jemalloc-sys = { version = "0.5.3", features = ["disable_initial_exec_tls"], optional = true } lazy_static = "1.1" libc = "0.2" -log = { version = "0.4", features = ["max_level_trace", "release_max_level_off"] } +log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"] } memoffset = "0.9" mimalloc-sys = { version = "0.1.6", optional = true } # MMTk macros - we have to specify a version here in order to publish the crate, even though we use the dependency from a local path. diff --git a/src/plan/sticky/immix/global.rs b/src/plan/sticky/immix/global.rs index c42d779d7a..ead55e1ebe 100644 --- a/src/plan/sticky/immix/global.rs +++ b/src/plan/sticky/immix/global.rs @@ -272,7 +272,7 @@ impl crate::plan::generational::global::GenerationalPlanExt f ); self.immix .immix_space - .trace_object_without_moving(queue, object) + .trace_object_without_moving(queue, object, false) }; return object; diff --git a/src/policy/immix/block.rs b/src/policy/immix/block.rs index 7f52a1e9ee..6561cb84ce 100644 --- a/src/policy/immix/block.rs +++ b/src/policy/immix/block.rs @@ -70,9 +70,9 @@ pub struct Block(Address); impl Region for Block { #[cfg(not(feature = "immix_smaller_block"))] - const LOG_BYTES: usize = 15; + const LOG_BYTES: usize = 14; #[cfg(feature = "immix_smaller_block")] - const LOG_BYTES: usize = 13; + const LOG_BYTES: usize = 14; fn from_aligned_address(address: Address) -> Self { debug_assert!(address.is_aligned_to(Self::BYTES)); diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index e2d787a274..1010509e41 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -33,6 +33,80 @@ use std::sync::{atomic::AtomicU8, atomic::AtomicUsize, Arc}; pub(crate) const TRACE_KIND_FAST: TraceKind = 0; pub(crate) const TRACE_KIND_DEFRAG: TraceKind = 1; +#[cfg(feature = "dump_memory_stats")] +#[derive(Default)] +/// Keeping track of the number of traced/copied/tpinned objects and live bytes +struct ImmixSpaceStats { + live_bytes: AtomicUsize, + traced_objects: AtomicUsize, + pinned_objects: AtomicUsize, + tpinned_objects: AtomicUsize, + copied_objects: AtomicUsize, +} + +#[cfg(feature = "dump_memory_stats")] +impl ImmixSpaceStats { + pub fn get_live_bytes(&self) -> usize { + self.live_bytes.load(Ordering::SeqCst) + } + + pub fn set_live_bytes(&self, size: usize) { + self.live_bytes.store(size, Ordering::SeqCst) + } + + pub fn increase_live_bytes(&self, size: usize) { + self.live_bytes.fetch_add(size, Ordering::SeqCst); + } + + pub fn get_traced_objects(&self) -> usize { + self.traced_objects.load(Ordering::SeqCst) + } + + pub fn set_traced_objects(&self, size: usize) { + self.traced_objects.store(size, Ordering::SeqCst) + } + + pub fn increase_traced_objects(&self, size: usize) { + self.traced_objects.fetch_add(size, Ordering::SeqCst); + } + + pub fn get_copied_objects(&self) -> usize { + self.copied_objects.load(Ordering::SeqCst) + } + + pub fn set_copied_objects(&self, size: usize) { + self.copied_objects.store(size, Ordering::SeqCst) + } + + pub fn increase_copied_objects(&self, size: usize) { + self.copied_objects.fetch_add(size, Ordering::SeqCst); + } + + pub fn get_pinned_objects(&self) -> usize { + self.pinned_objects.load(Ordering::SeqCst) + } + + pub fn set_pinned_objects(&self, size: usize) { + self.pinned_objects.store(size, Ordering::SeqCst) + } + + pub fn increase_pinned_objects(&self, size: usize) { + self.pinned_objects.fetch_add(size, Ordering::SeqCst); + } + + pub fn get_tpinned_objects(&self) -> usize { + self.tpinned_objects.load(Ordering::SeqCst) + } + + pub fn set_tpinned_objects(&self, size: usize) { + self.tpinned_objects.store(size, Ordering::SeqCst) + } + + pub fn increase_tpinned_objects(&self, size: usize) { + self.tpinned_objects.fetch_add(size, Ordering::SeqCst); + } +} + pub struct ImmixSpace { common: CommonSpace, pr: BlockPageResource, @@ -54,14 +128,9 @@ pub struct ImmixSpace { scheduler: Arc>, /// Some settings for this space space_args: ImmixSpaceArgs, - /// Keeping track of live bytes - #[cfg(feature = "dump_memory_stats")] - live_bytes: AtomicUsize, - /// Keeping track of the number of traced/copied objects + /// Keeping track of immix stats #[cfg(feature = "dump_memory_stats")] - live_objects: AtomicUsize, - #[cfg(feature = "dump_memory_stats")] - copied_objects: AtomicUsize, + immix_stats: ImmixSpaceStats, } /// Some arguments for Immix Space. @@ -194,7 +263,7 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace ) -> ObjectReference { debug_assert!(!object.is_null()); if KIND == TRACE_KIND_TRANSITIVE_PIN { - self.trace_object_without_moving(queue, object) + self.trace_object_without_moving(queue, object, true) } else if KIND == TRACE_KIND_DEFRAG { if Block::containing::(object).is_defrag_source() { debug_assert!(self.in_defrag()); @@ -211,10 +280,10 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace false, ) } else { - self.trace_object_without_moving(queue, object) + self.trace_object_without_moving(queue, object, false) } } else if KIND == TRACE_KIND_FAST { - self.trace_object_without_moving(queue, object) + self.trace_object_without_moving(queue, object, false) } else { unreachable!() } @@ -228,11 +297,11 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace // count the bytes for each object #[cfg(feature = "dump_memory_stats")] - self.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); + self.immix_stats.increase_live_bytes(VM::VMObjectModel::get_current_size(object)); // increase the number of objects scanned #[cfg(feature = "dump_memory_stats")] - self.increase_live_objects(1); + self.immix_stats.increase_traced_objects(1); } fn may_move_objects() -> bool { @@ -332,11 +401,7 @@ impl ImmixSpace { scheduler: scheduler.clone(), space_args, #[cfg(feature = "dump_memory_stats")] - live_bytes: AtomicUsize::new(0), - #[cfg(feature = "dump_memory_stats")] - live_objects: AtomicUsize::new(0), - #[cfg(feature = "dump_memory_stats")] - copied_objects: AtomicUsize::new(0), + immix_stats: Default::default() } } @@ -460,11 +525,13 @@ impl ImmixSpace { } #[cfg(feature = "dump_memory_stats")] - self.set_live_bytes(0); - #[cfg(feature = "dump_memory_stats")] - self.set_live_objects(0); - #[cfg(feature = "dump_memory_stats")] - self.set_copied_objects(0); + { + self.immix_stats.set_live_bytes(0); + self.immix_stats.set_traced_objects(0); + self.immix_stats.set_copied_objects(0); + self.immix_stats.set_tpinned_objects(0); + self.immix_stats.set_pinned_objects(0); + } } /// Release for the immix space. This is called when a GC finished. @@ -554,9 +621,11 @@ impl ImmixSpace { .expect("Time went backwards"); println!("{:?} mmtk_immixspace", since_the_epoch.as_millis()); - println!("\t#Live objects = {}", self.get_live_objects()); - println!("\t#Copied objects = {}", self.get_copied_objects()); - println!("\tLive bytes = {}", self.get_live_bytes()); + println!("\t#Live objects = {}", self.immix_stats.get_traced_objects()); + println!("\t#Copied objects = {}", self.immix_stats.get_copied_objects()); + println!("\t#Pinned objects = {}", self.immix_stats.get_pinned_objects()); + println!("\t#Transitively pinned objects = {}", self.immix_stats.get_tpinned_objects()); + println!("\tLive bytes = {}", self.immix_stats.get_live_bytes()); println!("\tReserved pages = {}", self.reserved_pages()); println!( "\tReserved pages (bytes) = {}", @@ -650,6 +719,7 @@ impl ImmixSpace { &self, queue: &mut impl ObjectQueue, object: ObjectReference, + _is_tpinned: bool ) -> ObjectReference { #[cfg(feature = "vo_bit")] vo_bit::helper::on_trace_object::(object); @@ -670,6 +740,14 @@ impl ImmixSpace { // Visit node queue.enqueue(object); self.unlog_object_if_needed(object); + + #[cfg(feature = "dump_memory_stats")] + if _is_tpinned { + // increase the number of objects being tpinned + #[cfg(feature = "dump_memory_stats")] + self.immix_stats.increase_tpinned_objects(1); + } + return object; } object @@ -724,6 +802,10 @@ impl ImmixSpace { object_forwarding::clear_forwarding_bits::(object); object } else { + #[cfg(feature = "dump_memory_stats")] + if self.is_pinned(object) { + self.immix_stats.increase_pinned_objects(1); + } // We won the forwarding race; actually forward and copy the object if it is not pinned // and we have sufficient space in our copy allocator let new_object = if self.is_pinned(object) @@ -748,7 +830,7 @@ impl ImmixSpace { // increase the number of objects being moved #[cfg(feature = "dump_memory_stats")] - self.increase_copied_objects(1); + self.immix_stats.increase_copied_objects(1); #[cfg(feature = "vo_bit")] vo_bit::helper::on_object_forwarded::(new_object); @@ -918,51 +1000,6 @@ impl ImmixSpace { self.mark_lines(object); } } - - #[cfg(feature = "dump_memory_stats")] - pub fn get_live_bytes(&self) -> usize { - self.live_bytes.load(Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn set_live_bytes(&self, size: usize) { - self.live_bytes.store(size, Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn increase_live_bytes(&self, size: usize) { - self.live_bytes.fetch_add(size, Ordering::SeqCst); - } - - #[cfg(feature = "dump_memory_stats")] - pub fn get_live_objects(&self) -> usize { - self.live_objects.load(Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn set_live_objects(&self, size: usize) { - self.live_objects.store(size, Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn increase_live_objects(&self, size: usize) { - self.live_objects.fetch_add(size, Ordering::SeqCst); - } - - #[cfg(feature = "dump_memory_stats")] - pub fn get_copied_objects(&self) -> usize { - self.copied_objects.load(Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn set_copied_objects(&self, size: usize) { - self.copied_objects.store(size, Ordering::SeqCst) - } - - #[cfg(feature = "dump_memory_stats")] - pub fn increase_copied_objects(&self, size: usize) { - self.copied_objects.fetch_add(size, Ordering::SeqCst); - } } /// A work packet to prepare each block for a major GC. diff --git a/src/policy/immix/mod.rs b/src/policy/immix/mod.rs index 7870a6b003..6746dbbe16 100644 --- a/src/policy/immix/mod.rs +++ b/src/policy/immix/mod.rs @@ -32,15 +32,15 @@ pub const DEFRAG: bool = !cfg!(feature = "immix_non_moving"); // defrag if we ar // | `DEFRAG_HEADROOM_PERCENT` | stress | `50` | Reserve enough headroom to copy all objects. 50% is like SemiSpace. | /// Make every GC a defragment GC. (for debugging) -pub const STRESS_DEFRAG: bool = false; +pub const STRESS_DEFRAG: bool = true; /// Mark every allocated block as defragmentation source before GC. (for debugging) -pub const DEFRAG_EVERY_BLOCK: bool = false; +pub const DEFRAG_EVERY_BLOCK: bool = true; /// Percentage of heap size reserved for defragmentation. /// According to [this paper](https://doi.org/10.1145/1375581.1375586), Immix works well with /// headroom between 1% to 3% of the heap size. -pub const DEFRAG_HEADROOM_PERCENT: usize = 2; +pub const DEFRAG_HEADROOM_PERCENT: usize = 50; /// If Immix is used as a nursery space, do we prefer copy? pub const PREFER_COPY_ON_NURSERY_GC: bool =