From 7af0b4258e09f7d45f42770a095089c3d76f25ef Mon Sep 17 00:00:00 2001 From: Matt Valentine-House Date: Thu, 17 Oct 2024 20:56:28 +0100 Subject: [PATCH] Move parsing MMTK_ variables into Rust --- gc/mmtk.c | 102 ------------------------------------------- gc/mmtk.h | 4 -- gc/mmtk/Cargo.lock | 74 +++++++++++++++++++++++++++++-- gc/mmtk/Cargo.toml | 1 + gc/mmtk/src/api.rs | 39 +++++++++++------ gc/mmtk/src/utils.rs | 71 ++++++++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 122 deletions(-) diff --git a/gc/mmtk.c b/gc/mmtk.c index 692e8a86a7a662..12947f5d8facdd 100644 --- a/gc/mmtk.c +++ b/gc/mmtk.c @@ -77,60 +77,6 @@ RB_THREAD_LOCAL_SPECIFIER struct MMTk_GCThreadTLS *rb_mmtk_gc_thread_tls; #include -static size_t -rb_mmtk_system_physical_memory(void) -{ -#ifdef __linux__ - const long physical_pages = sysconf(_SC_PHYS_PAGES); - const long page_size = sysconf(_SC_PAGE_SIZE); - if (physical_pages == -1 || page_size == -1) { - rb_bug("failed to get system physical memory size"); - } - return (size_t)physical_pages * (size_t)page_size; -#elif defined(__APPLE__) - int mib[2]; - mib[0] = CTL_HW; - mib[1] = HW_MEMSIZE; // total physical memory - int64_t physical_memory; - size_t length = sizeof(int64_t); - if (sysctl(mib, 2, &physical_memory, &length, NULL, 0) == -1) { - rb_bug("failed to get system physical memory size"); - } - return (size_t)physical_memory; -#else -#error no implementation of rb_mmtk_system_physical_memory on this platform -#endif -} - -static size_t -rb_mmtk_parse_heap_limit(const char *argv, bool *had_error) -{ - char *endval = NULL; - int pow = 0; - - size_t base = strtol(argv, &endval, 10); - if (base == 0) { - *had_error = true; - } - - // if there were non-numbers in the string - // try and parse them as IEC units - if (*endval) { - if (strcmp(endval, "TiB") == 0) { - pow = 40; // tebibytes. 2^40 - } else if (strcmp(endval, "GiB") == 0) { - pow = 30; // gibibytes. 2^30 - } else if (strcmp(endval, "MiB") == 0) { - pow = 20; // mebibytes. 2^20 - } else if (strcmp(endval, "KiB") == 0) { - pow = 10; // kibibytes. 2^10 - } - } - - *had_error = false; - return (base << pow); -} - static void rb_mmtk_init_gc_worker_thread(MMTk_VMWorkerThread gc_thread_tls) { @@ -489,54 +435,6 @@ MMTk_Builder * rb_mmtk_builder_init(void) { MMTk_Builder *builder = mmtk_builder_default(); - - size_t mmtk_max_heap_size = RB_MMTK_DEFAULT_HEAP_MAX; - size_t mmtk_min_heap_size = RB_MMTK_DEFAULT_HEAP_MIN; - const char *mmtk_max_heap_size_arg; - const char *mmtk_min_heap_size_arg; - enum mmtk_heap_mode mode = RB_MMTK_DYNAMIC_HEAP; - - // switch to fixed mode if defined, for any other value assume the default, which is dynamic - if (getenv("MMTK_HEAP_MODE") && !strcmp(getenv("MMTK_HEAP_MODE"), "fixed")) { - mode = RB_MMTK_FIXED_HEAP; - } - - if ((mmtk_max_heap_size_arg = getenv("MMTK_HEAP_MAX"))) { - bool max_error = true; - mmtk_max_heap_size = rb_mmtk_parse_heap_limit(mmtk_max_heap_size_arg, &max_error); - if (max_error) { - rb_warn("MMTk maximum heap size invalid, using default"); - mmtk_max_heap_size = RB_MMTK_DEFAULT_HEAP_MAX; - } - } - - if ((mmtk_min_heap_size_arg = getenv("MMTK_HEAP_MIN"))) { - bool min_error; - mmtk_min_heap_size = rb_mmtk_parse_heap_limit(mmtk_min_heap_size_arg, &min_error); - if (min_error) { - rb_warn("MMTk minimum heap size invalid, using default"); - mmtk_min_heap_size = RB_MMTK_DEFAULT_HEAP_MIN; - } - }; - - switch (mode) { - case RB_MMTK_FIXED_HEAP: - mmtk_set_fixed_heap(builder, mmtk_max_heap_size); - break; - case RB_MMTK_DYNAMIC_HEAP: - if (mmtk_min_heap_size >= mmtk_max_heap_size) { - rb_bug( - "MMTK Heap size invalid: please set max heap (%zu) larger than min heap (%zu)", - mmtk_max_heap_size, mmtk_min_heap_size - ); - } - mmtk_set_dynamic_heap(builder, mmtk_min_heap_size, mmtk_max_heap_size); - break; - default: - rb_bug("Unreachable: Invalid MMTK Heap Mode"); - break; - } - return builder; } diff --git a/gc/mmtk.h b/gc/mmtk.h index 8c68c5701ca8eb..09802499b898f2 100644 --- a/gc/mmtk.h +++ b/gc/mmtk.h @@ -98,10 +98,6 @@ void mmtk_init_binding(MMTk_Builder *builder, const struct MMTk_RubyUpcalls *upcalls, MMTk_ObjectReference weak_reference_dead_value); -void mmtk_set_fixed_heap(MMTk_Builder *builder, size_t max_heap_size); - -void mmtk_set_dynamic_heap(MMTk_Builder *builder, size_t min_heap_size, size_t max_heap_size); - void mmtk_initialize_collection(MMTk_VMThread tls); MMTk_Mutator *mmtk_bind_mutator(MMTk_VMMutatorThread tls); diff --git a/gc/mmtk/Cargo.lock b/gc/mmtk/Cargo.lock index ef750d61e0058c..62a7a5669bf58c 100644 --- a/gc/mmtk/Cargo.lock +++ b/gc/mmtk/Cargo.lock @@ -462,7 +462,7 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "sysinfo", + "sysinfo 0.30.12", ] [[package]] @@ -488,6 +488,7 @@ dependencies = [ "mmtk", "once_cell", "probe", + "sysinfo 0.32.0", ] [[package]] @@ -733,7 +734,21 @@ dependencies = [ "ntapi", "once_cell", "rayon", - "windows", + "windows 0.52.0", +] + +[[package]] +name = "sysinfo" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", ] [[package]] @@ -829,7 +844,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", "windows-targets", ] @@ -842,6 +867,49 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/gc/mmtk/Cargo.toml b/gc/mmtk/Cargo.toml index 2b3282f4ae5e8f..a1da874fc5b019 100644 --- a/gc/mmtk/Cargo.toml +++ b/gc/mmtk/Cargo.toml @@ -24,6 +24,7 @@ env_logger = "0.11.3" once_cell = "1.17.0" atomic_refcell = "0.1.9" probe = "0.5" +sysinfo = "0.32.0" [dependencies.mmtk] features = ["is_mmtk_object", "object_pinning", "sticky_immix_non_moving_nursery"] diff --git a/gc/mmtk/src/api.rs b/gc/mmtk/src/api.rs index 88dcb82683cd2e..12d33e5f04a36c 100644 --- a/gc/mmtk/src/api.rs +++ b/gc/mmtk/src/api.rs @@ -9,6 +9,7 @@ use crate::binding::RubyBinding; use crate::mmtk; use crate::Ruby; use crate::RubySlot; +use crate::utils::default_heap_max; use mmtk::memory_manager; use mmtk::memory_manager::mmtk_init; use mmtk::util::constants::MIN_OBJECT_SIZE; @@ -40,6 +41,30 @@ pub extern "C" fn mmtk_builder_default() -> *mut MMTKBuilder { let mut builder = MMTKBuilder::new_no_env_vars(); builder.options.no_finalizer.set(true); + + const DEFAULT_HEAP_MIN: usize = 1 << 20; + + let mut mmtk_heap_min = match std::env::var("MMTK_HEAP_MIN") { + Ok(mode) if (mode.parse::().is_ok()) => mode.parse::().unwrap(), + Ok(_) | Err(_) => DEFAULT_HEAP_MIN + }; + + let mut mmtk_heap_max = match std::env::var("MMTK_HEAP_MAX") { + Ok(mode) if (mode.parse::().is_ok()) => mode.parse::().unwrap(), + Ok(_) | Err(_) => default_heap_max() + }; + + if mmtk_heap_min >= mmtk_heap_max { + println!("MMTK_HEAP_MIN({}) >= MMTK_HEAP_MAX({}). Using default values.", mmtk_heap_min, mmtk_heap_max); + mmtk_heap_min = DEFAULT_HEAP_MIN; + mmtk_heap_max = default_heap_max(); + } + + let mmtk_mode = match std::env::var("MMTK_HEAP_MODE") { + Ok(mode) if (mode == "fixed") => GCTriggerSelector::FixedHeapSize(mmtk_heap_max), + Ok(_) | Err(_) => GCTriggerSelector::DynamicHeapSize(mmtk_heap_min, mmtk_heap_max) + }; + // Parse the env var, if it's not found set the plan name to MarkSweep let plan_name = std::env::var("MMTK_PLAN") .unwrap_or(String::from("MarkSweep")); @@ -51,7 +76,7 @@ pub extern "C" fn mmtk_builder_default() -> *mut MMTKBuilder { builder.options.plan.set(plan_selector); // Between 1MiB and 500MiB - builder.options.gc_trigger.set(GCTriggerSelector::DynamicHeapSize(1 << 20, 500 << 20)); + builder.options.gc_trigger.set(mmtk_mode); Box::into_raw(Box::new(builder)) } @@ -77,18 +102,6 @@ pub extern "C" fn mmtk_init_binding( .unwrap_or_else(|_| panic!("Binding is already initialized")); } -#[no_mangle] -pub extern "C" fn mmtk_set_fixed_heap(builder: *mut MMTKBuilder, max_heap_size: usize) { - let builder = unsafe { &mut *builder }; - builder.options.gc_trigger.set(GCTriggerSelector::FixedHeapSize(max_heap_size)); -} - -#[no_mangle] -pub extern "C" fn mmtk_set_dynamic_heap(builder: *mut MMTKBuilder, min_heap_size: usize, max_heap_size: usize) { - let builder = unsafe { &mut *builder }; - builder.options.gc_trigger.set(GCTriggerSelector::DynamicHeapSize(min_heap_size, max_heap_size)); -} - #[no_mangle] pub extern "C" fn mmtk_initialize_collection(tls: VMThread) { memory_manager::initialize_collection(mmtk(), tls) diff --git a/gc/mmtk/src/utils.rs b/gc/mmtk/src/utils.rs index b61973cbfe0344..5915842362064a 100644 --- a/gc/mmtk/src/utils.rs +++ b/gc/mmtk/src/utils.rs @@ -3,6 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use atomic_refcell::AtomicRefCell; use mmtk::scheduler::{GCWork, GCWorker, WorkBucketStage}; +use sysinfo::System; use crate::Ruby; pub struct ChunkedVecCollector { @@ -86,3 +87,73 @@ impl AfterAll { } } } + +pub fn default_heap_max() -> usize { + let mut s = System::new(); + s.refresh_memory(); + s.total_memory() + .checked_mul(80) + .and_then(|v| v.checked_div(100)) + .expect("Invalid Memory size") as usize +} + +pub fn parse_capacity(input: String) -> usize { + let trimmed = input.trim(); + + const KIBIBYTE: usize = 1024; + const MEBIBYTE: usize = 1024 * KIBIBYTE; + const GIBIBYTE: usize = 1024 * MEBIBYTE; + + let (val, suffix) = if let Some(pos) = trimmed.find(|c: char| !c.is_numeric()) { + (&trimmed[..pos], &trimmed[pos..]) + } else { + (trimmed, "") + }; + + // 1MiB is the default heap size + match (val, suffix) { + (number, "GiB") => number.parse::().unwrap() * GIBIBYTE, + (number, "MiB") => number.parse::().unwrap() * MEBIBYTE, + (number, "KiB") => number.parse::().unwrap() * KIBIBYTE, + (number, suffix) if suffix.is_empty() => number.parse::().unwrap(), + (_, _) => default_heap_max() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_capacity_parses_bare_bytes() { + assert_eq!(1234, parse_capacity(String::from("1234"))); + } + + #[test] + fn test_parse_capacity_parses_kibibytes() { + assert_eq!(10240, parse_capacity(String::from("10KiB"))) + } + + #[test] + fn test_parse_capacity_parses_mebibytes() { + assert_eq!(10485760, parse_capacity(String::from("10MiB"))) + } + + #[test] + fn test_parse_capacity_parses_gibibytes() { + assert_eq!(10737418240, parse_capacity(String::from("10GiB"))) + } + + #[test] + fn test_parses_nonsense_value_as_default_max() { + let mut s = System::new(); + s.refresh_memory(); + let eighty_percent_mem = (s.total_memory() / 100 * 80) as usize; + + assert_eq!(eighty_percent_mem, parse_capacity(String::from("notanumber"))); + assert_eq!(eighty_percent_mem, parse_capacity(String::from("5tartswithanumber"))); + assert_eq!(eighty_percent_mem, parse_capacity(String::from("number1nthemiddle"))); + assert_eq!(eighty_percent_mem, parse_capacity(String::from("numberattheend111"))); + assert_eq!(eighty_percent_mem, parse_capacity(String::from("mult1pl3numb3r5"))); + } +}