Skip to content

Commit 71969f3

Browse files
authored
Generational code cache roots (#282)
This PR ports the generational code cache roots handling code from the LXR branch. See https://github.com/wenyuzhao/mmtk-openjdk/blob/lxr/mmtk/src/gc_work.rs#L242-L262 We split code cache roots into two hash tables, one for nursery roots and one for mature roots. Nursery roots are those added since the previous GC. In a nursery GC, we only scan the nursery code cache roots, assuming mature code cache roots all point to mature objects. In a full-heap GC, we scan both nursery and mature code cache roots. Because code cache roots are added in bulk in the beginning of execution, and are seldom added later, we expect the number of code cache roots to reduce to zero for most nursery GCs, and greatly reduce the root-scanning time. This PR also adds USDT tracepoints for code cache roots, and make use of the extensible eBPF timeline tools in mmtk-core introduced in mmtk/mmtk-core#1162.
1 parent 106b924 commit 71969f3

File tree

9 files changed

+103
-54
lines changed

9 files changed

+103
-54
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ target/
1515
/*.dylib
1616
/*.so
1717

18-
/repos/mmtk-core
18+
/repos/mmtk-core
19+
20+
__pycache__

mmtk/Cargo.lock

+7-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mmtk/Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "mmtk_openjdk"
33
version = "0.27.0"
44
authors = [" <>"]
5-
rust-version = "1.71.1"
5+
rust-version = "1.73.0"
66
build = "build.rs"
77
edition = "2021"
88

@@ -27,14 +27,15 @@ once_cell = "1.10.0"
2727
atomic = "0.6.0"
2828
memoffset = "0.9.0"
2929
cfg-if = "1.0"
30+
probe = "0.5"
3031

3132
# Be very careful to commit any changes to the following mmtk dependency, as our CI scripts (including mmtk-core CI)
3233
# rely on matching these lines to modify them: e.g. comment out the git dependency and use the local path.
3334
# These changes are safe:
3435
# - change branch
3536
# - change repo name
3637
# But other changes including adding/removing whitespaces in commented lines may break the CI.
37-
mmtk = { git = "https://github.com/mmtk/mmtk-core.git", rev = "45cdf31055b1b6a629bdb8032adaa6dd5a8e32b9" }
38+
mmtk = { git = "https://github.com/mmtk/mmtk-core.git", rev = "a025c24104d8d456a865aa0122e6e0fb6d77e8f2" }
3839
# Uncomment the following to build locally
3940
# mmtk = { path = "../repos/mmtk-core" }
4041

mmtk/src/api.rs

+21-29
Original file line numberDiff line numberDiff line change
@@ -490,49 +490,41 @@ pub extern "C" fn get_finalized_object() -> NullableObjectReference {
490490
}
491491

492492
thread_local! {
493-
/// Cache all the pointers reported by the current thread.
493+
/// Cache reference slots of an nmethod while the current thread is executing
494+
/// `MMTkRegisterNMethodOopClosure`.
494495
static NMETHOD_SLOTS: RefCell<Vec<Address>> = const { RefCell::new(vec![]) };
495496
}
496497

497-
/// Report a list of pointers in nmethod to mmtk.
498+
/// Report one reference slot in an nmethod to MMTk.
498499
#[no_mangle]
499500
pub extern "C" fn mmtk_add_nmethod_oop(addr: Address) {
500-
NMETHOD_SLOTS.with(|x| x.borrow_mut().push(addr))
501+
NMETHOD_SLOTS.with_borrow_mut(|x| x.push(addr))
501502
}
502503

503-
/// Register a nmethod.
504-
/// The c++ part of the binding should scan the nmethod and report all the pointers to mmtk first, before calling this function.
505-
/// This function will transfer all the locally cached pointers of this nmethod to the global storage.
504+
/// Register an nmethod.
505+
///
506+
/// The C++ part of the binding should have scanned the nmethod and reported all the reference slots
507+
/// using `mmtk_add_nmethod_oop` before calling this function. This function will transfer all the
508+
/// locally cached slots of this nmethod to the global storage.
506509
#[no_mangle]
507510
pub extern "C" fn mmtk_register_nmethod(nm: Address) {
508-
let slots = NMETHOD_SLOTS.with(|x| {
509-
if x.borrow().len() == 0 {
510-
return None;
511+
NMETHOD_SLOTS.with_borrow_mut(|slots| {
512+
if !slots.is_empty() {
513+
let mut roots = crate::NURSERY_CODE_CACHE_ROOTS.lock().unwrap();
514+
roots.insert(nm, std::mem::take(slots));
511515
}
512-
Some(x.replace(vec![]))
513516
});
514-
let slots = match slots {
515-
Some(slots) => slots,
516-
_ => return,
517-
};
518-
let mut roots = crate::CODE_CACHE_ROOTS.lock().unwrap();
519-
// Relaxed add instead of `fetch_add`, since we've already acquired the lock.
520-
crate::CODE_CACHE_ROOTS_SIZE.store(
521-
crate::CODE_CACHE_ROOTS_SIZE.load(Ordering::Relaxed) + slots.len(),
522-
Ordering::Relaxed,
523-
);
524-
roots.insert(nm, slots);
525517
}
526518

527-
/// Unregister a nmethod.
519+
/// Unregister an nmethod.
528520
#[no_mangle]
529521
pub extern "C" fn mmtk_unregister_nmethod(nm: Address) {
530-
let mut roots = crate::CODE_CACHE_ROOTS.lock().unwrap();
531-
if let Some(slots) = roots.remove(&nm) {
532-
// Relaxed sub instead of `fetch_sub`, since we've already acquired the lock.
533-
crate::CODE_CACHE_ROOTS_SIZE.store(
534-
crate::CODE_CACHE_ROOTS_SIZE.load(Ordering::Relaxed) - slots.len(),
535-
Ordering::Relaxed,
536-
);
522+
{
523+
let mut roots = crate::NURSERY_CODE_CACHE_ROOTS.lock().unwrap();
524+
roots.remove(&nm);
525+
}
526+
{
527+
let mut roots = crate::MATURE_CODE_CACHE_ROOTS.lock().unwrap();
528+
roots.remove(&nm);
537529
}
538530
}

mmtk/src/gc_work.rs

+44-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::sync::atomic::Ordering;
2-
1+
use crate::scanning;
32
use crate::scanning::to_slots_closure;
43
use crate::OpenJDK;
54
use crate::OpenJDKSlot;
65
use crate::UPCALLS;
76
use mmtk::scheduler::*;
7+
use mmtk::util::Address;
88
use mmtk::vm::RootsWorkFactory;
99
use mmtk::vm::*;
1010
use mmtk::MMTK;
@@ -69,16 +69,51 @@ impl<const COMPRESSED: bool, F: RootsWorkFactory<OpenJDKSlot<COMPRESSED>>>
6969
fn do_work(
7070
&mut self,
7171
_worker: &mut GCWorker<OpenJDK<COMPRESSED>>,
72-
_mmtk: &'static MMTK<OpenJDK<COMPRESSED>>,
72+
mmtk: &'static MMTK<OpenJDK<COMPRESSED>>,
7373
) {
74-
// Collect all the cached roots
75-
let mut slots = Vec::with_capacity(crate::CODE_CACHE_ROOTS_SIZE.load(Ordering::Relaxed));
76-
for roots in (*crate::CODE_CACHE_ROOTS.lock().unwrap()).values() {
77-
for r in roots {
78-
slots.push((*r).into())
74+
let is_current_gc_nursery = mmtk
75+
.get_plan()
76+
.generational()
77+
.is_some_and(|gen| gen.is_current_gc_nursery());
78+
79+
let mut slots = Vec::with_capacity(scanning::WORK_PACKET_CAPACITY);
80+
81+
let mut nursery_slots = 0;
82+
let mut mature_slots = 0;
83+
84+
let mut add_roots = |roots: &[Address]| {
85+
for root in roots {
86+
slots.push(OpenJDKSlot::<COMPRESSED>::from(*root));
87+
if slots.len() >= scanning::WORK_PACKET_CAPACITY {
88+
self.factory
89+
.create_process_roots_work(std::mem::take(&mut slots));
90+
}
91+
}
92+
};
93+
94+
{
95+
let mut mature = crate::MATURE_CODE_CACHE_ROOTS.lock().unwrap();
96+
97+
// Only scan mature roots in full-heap collections.
98+
if !is_current_gc_nursery {
99+
for roots in mature.values() {
100+
mature_slots += roots.len();
101+
add_roots(roots);
102+
}
103+
}
104+
105+
{
106+
let mut nursery = crate::NURSERY_CODE_CACHE_ROOTS.lock().unwrap();
107+
for (key, roots) in nursery.drain() {
108+
nursery_slots += roots.len();
109+
add_roots(&roots);
110+
mature.insert(key, roots);
111+
}
79112
}
80113
}
81-
// Create work packet
114+
115+
probe!(mmtk_openjdk, code_cache_roots, nursery_slots, mature_slots);
116+
82117
if !slots.is_empty() {
83118
self.factory.create_process_roots_work(slots);
84119
}

mmtk/src/lib.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#[macro_use]
22
extern crate lazy_static;
3+
#[macro_use]
4+
extern crate probe;
35

46
use std::collections::HashMap;
57
use std::ptr::null_mut;
6-
use std::sync::atomic::AtomicUsize;
78
use std::sync::Mutex;
89

910
use libc::{c_char, c_void, uintptr_t};
@@ -202,13 +203,12 @@ pub static MMTK_MARK_COMPACT_HEADER_RESERVED_IN_BYTES: usize =
202203
mmtk::util::alloc::MarkCompactAllocator::<OpenJDK<false>>::HEADER_RESERVED_IN_BYTES;
203204

204205
lazy_static! {
205-
/// A global storage for all the cached CodeCache root pointers
206-
static ref CODE_CACHE_ROOTS: Mutex<HashMap<Address, Vec<Address>>> = Mutex::new(HashMap::new());
206+
/// A global storage for all the cached CodeCache roots added since the last GC.
207+
static ref NURSERY_CODE_CACHE_ROOTS: Mutex<HashMap<Address, Vec<Address>>> = Mutex::new(HashMap::new());
208+
/// A global storage for all the cached CodeCache roots added before the last GC.
209+
static ref MATURE_CODE_CACHE_ROOTS: Mutex<HashMap<Address, Vec<Address>>> = Mutex::new(HashMap::new());
207210
}
208211

209-
/// A counter tracking the total size of the `CODE_CACHE_ROOTS`.
210-
static CODE_CACHE_ROOTS_SIZE: AtomicUsize = AtomicUsize::new(0);
211-
212212
fn set_compressed_pointer_vm_layout(builder: &mut MMTKBuilder) {
213213
let max_heap_size = builder.options.gc_trigger.max_heap_size();
214214
assert!(

mmtk/src/scanning.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use mmtk::MutatorContext;
1212

1313
pub struct VMScanning {}
1414

15-
const WORK_PACKET_CAPACITY: usize = 4096;
15+
pub(crate) const WORK_PACKET_CAPACITY: usize = 4096;
1616

1717
extern "C" fn report_slots_and_renew_buffer<S: Slot, F: RootsWorkFactory<S>>(
1818
ptr: *mut Address,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
usdt:$MMTK:mmtk_openjdk:code_cache_roots {
2+
if (@enable_print) {
3+
printf("code_cache_roots,meta,%d,%lu,%lu,%lu\n", tid, nsecs, arg0, arg1);
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
3+
def enrich_meta_extra(log_processor, name, tid, ts, gc, wp, args):
4+
if wp is not None:
5+
match name:
6+
case "code_cache_roots":
7+
nursery, mature = int(args[0]), int(args[1])
8+
total = nursery + mature
9+
wp["args"] |= {
10+
"nursery_slots": nursery,
11+
"mature_slots": mature,
12+
"total_slots": total,
13+
}

0 commit comments

Comments
 (0)