Skip to content

Commit

Permalink
Move parsing MMTK_ variables into Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
eightbitraptor committed Oct 17, 2024
1 parent d113102 commit 7af0b42
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 122 deletions.
102 changes: 0 additions & 102 deletions gc/mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,60 +77,6 @@ RB_THREAD_LOCAL_SPECIFIER struct MMTk_GCThreadTLS *rb_mmtk_gc_thread_tls;

#include <pthread.h>

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)
{
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 0 additions & 4 deletions gc/mmtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
74 changes: 71 additions & 3 deletions gc/mmtk/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gc/mmtk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
39 changes: 26 additions & 13 deletions gc/mmtk/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<usize>().is_ok()) => mode.parse::<usize>().unwrap(),
Ok(_) | Err(_) => DEFAULT_HEAP_MIN
};

let mut mmtk_heap_max = match std::env::var("MMTK_HEAP_MAX") {
Ok(mode) if (mode.parse::<usize>().is_ok()) => mode.parse::<usize>().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"));
Expand All @@ -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))
}
Expand All @@ -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)
Expand Down
71 changes: 71 additions & 0 deletions gc/mmtk/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
Expand Down Expand Up @@ -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::<usize>().unwrap() * GIBIBYTE,
(number, "MiB") => number.parse::<usize>().unwrap() * MEBIBYTE,
(number, "KiB") => number.parse::<usize>().unwrap() * KIBIBYTE,
(number, suffix) if suffix.is_empty() => number.parse::<usize>().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")));
}
}

0 comments on commit 7af0b42

Please sign in to comment.