Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal pointer support #1165

Merged
merged 27 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b9beda8
Add methods to find non zero value from contiguous side metadata.
qinsoon Jun 26, 2024
b28c077
Require object reference to be aligned
qinsoon Jun 27, 2024
3ea862a
Update docs
qinsoon Jul 2, 2024
26d7893
Allow access raw byte/word from side metadata. Find base reference using
qinsoon Jul 4, 2024
60b8163
Merge remote-tracking branch 'my-fork/aligned-obj-ref' into feature/i…
qinsoon Jul 5, 2024
f4363a9
Some basic tests. Use SFT to dispatch calls to find base reference.
qinsoon Jul 8, 2024
6a3610a
Add micro benchmarks for internal pointers
qinsoon Jul 8, 2024
e6f82a6
Try load word when we search back
qinsoon Jul 9, 2024
20716ac
Some cleanup
qinsoon Jul 9, 2024
9dac372
Tidy up. More tests.
qinsoon Jul 10, 2024
2497124
Remove the use of Option::inspect for Rust 1.71
qinsoon Jul 10, 2024
6ce0d2c
Introduce a constant IN_OBJECT_ADDRESS_OFFSET
qinsoon Jul 10, 2024
80253fd
Fix missing imports in DummyVM
qinsoon Jul 10, 2024
c284fe5
Fix a few issues in the comments
qinsoon Jul 10, 2024
3aee387
Apply cargo fmt to dummyvm
qinsoon Jul 10, 2024
29dfce4
Minor changes to ObjectReference based on the review
qinsoon Jul 10, 2024
d977063
Minor updates to the docs on is_mmtk_object
qinsoon Jul 11, 2024
55a2f00
Merge remote-tracking branch 'my-fork/aligned-obj-ref' into feature/i…
qinsoon Jul 11, 2024
f0bc857
Address reviews.
qinsoon Jul 11, 2024
ac49cad
Fix bench
qinsoon Jul 11, 2024
61c8d89
Derive Eq for PlanSelector
qinsoon Jul 11, 2024
1bb2d14
Merge branch 'master' into feature/internal-pointer-fast
qinsoon Jul 11, 2024
de05ffa
Add migration guide for this PR, and add missing links for last PR.
qinsoon Jul 11, 2024
3cd2a99
Cargo fmt
qinsoon Jul 12, 2024
da90e2b
Fix broken doc link
qinsoon Jul 12, 2024
8f7670a
Fix a few more issues from reviews
qinsoon Jul 18, 2024
9fa6530
Move the implementation of is_mmtk_objct and
qinsoon Jul 18, 2024
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
1 change: 1 addition & 0 deletions benches/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub fn bench_main(_c: &mut Criterion) {
match std::env::var("MMTK_BENCH") {
Ok(bench) => match bench.as_str() {
"alloc" => mock_bench::alloc::bench(_c),
"internal_pointer" => mock_bench::internal_pointer::bench(_c),
"sft" => mock_bench::sft::bench(_c),
_ => panic!("Unknown benchmark {:?}", bench),
},
Expand Down
97 changes: 97 additions & 0 deletions benches/mock_bench/internal_pointer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use criterion::Criterion;

#[cfg(feature = "is_mmtk_object")]
use mmtk::util::test_util::fixtures::*;
use mmtk::util::test_util::mock_method::*;
use mmtk::util::test_util::mock_vm::{write_mockvm, MockVM};

pub fn bench(c: &mut Criterion) {
// Setting a larger heap, although the GC should be disabled in the MockVM
#[cfg(feature = "is_mmtk_object")]
let mut fixture = MutatorFixture::create_with_heapsize(1 << 30);

// Normal objects
// 16KB object -- we want to make sure the object can fit into any normal space (e.g. immix space or mark sweep space)
const NORMAL_OBJECT_SIZE: usize = 16 * 1024;
write_mockvm(|mock| {
*mock = MockVM {
get_object_size: MockMethod::new_fixed(Box::new(|_| NORMAL_OBJECT_SIZE)),
is_collection_enabled: MockMethod::new_fixed(Box::new(|_| false)),
..MockVM::default()
}
});

c.bench_function("internal pointer - normal objects", |_b| {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some data for this micro benchmark.

The naive implementation (find_prev_non_zero_value_simple, similar to #1155): check every aligned data address until we find the object.

internal pointer - normal objects
                        time:   [12.910 µs 12.911 µs 12.912 µs]

internal pointer - large objects
                        time:   [53.164 ns 53.486 ns 53.939 ns]

Check every byte in side metadata until we find VO bit, and compute it back to the VO address

internal pointer - normal objects
                        time:   [1.5500 µs 1.5504 µs 1.5509 µs]
                        change: [-88.001% -87.997% -87.994%] (p = 0.00 < 0.05)
                        Performance has improved.

internal pointer - large objects
                        time:   [58.115 ns 58.137 ns 58.161 ns]
                        change: [+8.0877% +8.6753% +9.1317%] (p = 0.00 < 0.05)
                        Performance has regressed.

Check every word in side metadata until we find VO bit, and compute it back to the VO address

internal pointer - normal objects
                        time:   [311.28 ns 312.38 ns 313.51 ns]
                        change: [-79.856% -79.804% -79.741%] (p = 0.00 < 0.05)
                        Performance has improved.

internal pointer - large objects
                        time:   [57.582 ns 57.607 ns 57.635 ns]
                        change: [-0.9006% -0.8311% -0.7566%] (p = 0.00 < 0.05)
                        Change within noise threshold.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After applying the suggestion in #1165 (comment), we only check if the metadata address is mapped for every chunk. The performance is further improved.

internal pointer - normal objects
                        time:   [139.48 ns 139.65 ns 139.82 ns]
                        change: [-55.524% -55.388% -55.250%] (p = 0.00 < 0.05)
                        Performance has improved.

internal pointer - large objects
                        time:   [49.300 ns 49.361 ns 49.462 ns]
                        change: [-14.459% -14.367% -14.263%] (p = 0.00 < 0.05)
                        Performance has improved.

#[cfg(feature = "is_mmtk_object")]
{
use mmtk::memory_manager;
use mmtk::vm::ObjectModel;
use mmtk::AllocationSemantics;
let addr = memory_manager::alloc(
&mut fixture.mutator,
NORMAL_OBJECT_SIZE,
8,
0,
AllocationSemantics::Default,
);
let obj_ref = MockVM::address_to_ref(addr);
memory_manager::post_alloc(
&mut fixture.mutator,
obj_ref,
NORMAL_OBJECT_SIZE,
AllocationSemantics::Default,
);
let obj_end = addr + NORMAL_OBJECT_SIZE;
_b.iter(|| {
memory_manager::find_object_from_internal_pointer::<MockVM>(
obj_end - 1,
NORMAL_OBJECT_SIZE,
);
})
}
#[cfg(not(feature = "is_mmtk_object"))]
panic!("The benchmark requires is_mmtk_object feature to run");
});

// Large objects
// 16KB object
const LARGE_OBJECT_SIZE: usize = 16 * 1024;
write_mockvm(|mock| {
*mock = MockVM {
get_object_size: MockMethod::new_fixed(Box::new(|_| LARGE_OBJECT_SIZE)),
is_collection_enabled: MockMethod::new_fixed(Box::new(|_| false)),
..MockVM::default()
}
});
c.bench_function("internal pointer - large objects", |_b| {
#[cfg(feature = "is_mmtk_object")]
{
use mmtk::memory_manager;
use mmtk::vm::ObjectModel;
use mmtk::AllocationSemantics;
let addr = memory_manager::alloc(
&mut fixture.mutator,
LARGE_OBJECT_SIZE,
8,
0,
AllocationSemantics::Los,
);
let obj_ref = MockVM::address_to_ref(addr);
memory_manager::post_alloc(
&mut fixture.mutator,
obj_ref,
LARGE_OBJECT_SIZE,
AllocationSemantics::Los,
);
let obj_end = addr + LARGE_OBJECT_SIZE;
_b.iter(|| {
memory_manager::find_object_from_internal_pointer::<MockVM>(
obj_end - 1,
LARGE_OBJECT_SIZE,
);
})
}
#[cfg(not(feature = "is_mmtk_object"))]
panic!("The benchmark requires is_mmtk_object feature to run");
});
}
1 change: 1 addition & 0 deletions benches/mock_bench/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod alloc;
pub mod internal_pointer;
pub mod sft;
44 changes: 44 additions & 0 deletions src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,50 @@ pub fn is_mmtk_object(addr: Address) -> bool {
SFT_MAP.get_checked(addr).is_mmtk_object(addr)
}

/// Find if there is an object with VO bit set for the given address range.
/// This should be used instead of [`crate::memory_manager::is_mmtk_object`] for conservative stack scanning if
/// the binding may have internal pointers on the stack.
///
/// The function cannot directly return an object reference. Instead, it returns
/// an address range and guarantees the object ference is in the range.
/// The reason behind this is that we use the VO
/// bit to represent a valid object for every 8 bytes ([`crate::util::is_mmtk_object::VO_BIT_REGION_SIZE`]).
/// We use VO bits to find the object for an internal pointer.
/// When we find a set VO bit, we only know that in the 8 bytes there is an object, and we cannot know where
/// exactly the object is. The binding needs to use their knowledge about the alignment
/// and offset for object references to find out the object reference from the return value.
/// See Case 2 in [`crate::memory_manager::is_mmtk_object`] for more explanation.
///
/// Note that, we only consider pointers that points to an address that is equal or after the in-object addresss
/// (i.e. the address returned from [`crate::vm::ObjectModel::ref_to_address`]), and within the allocation
/// as 'internal pointers'. To be precise, for each object ref `obj_ref`, internal pointers are in the range
/// `[ObjectModel::ref_to_address(obj_ref), ObjectModel::ref_to_object_start(obj_ref) + ObjectModel::get_current_size(obj_ref))`.
/// If a binding defines internal pointers differently, calling this method is undefined behavior.
/// If this is the case for you, please submit an issue or engage us on Zulip to discuss more.
///
/// Note that, in the similar situation as [`crate::memory_manager::is_mmtk_object`], the binding should filter
/// out obvious non-pointers (e.g. alignment check, bound check, etc) before calling this function to avoid unnecessary
/// cost. This method is not cheap.
///
/// To minimize the cost, the user should also use a small `max_search_bytes`.
///
/// Argument:
/// * `internal_ptr`: The internal pointer to start. We search backwards from this internal pointer to find the base reference.
/// * `max_search_bytes`: The maximum number of bytes we may search for an object with VO bit set.
#[cfg(feature = "is_mmtk_object")]
pub fn find_object_from_internal_pointer<VM: VMBinding>(
internal_ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
use crate::mmtk::SFT_MAP;
let ret = SFT_MAP
.get_checked(internal_ptr)
.find_object_from_internal_pointer(internal_ptr, max_search_bytes);
#[cfg(debug_assertions)]
ret.inspect(|obj| debug_assert!(is_mmtk_object(obj.to_raw_address())));
ret
}

/// Return true if the `object` lies in a region of memory where
/// - only MMTk can allocate into, or
/// - only MMTk's delegated memory allocator (such as a malloc implementation) can allocate into
Expand Down
12 changes: 12 additions & 0 deletions src/policy/copyspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ impl<VM: VMBinding> SFT for CopySpace<VM> {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}

#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}

fn sft_trace_object(
&self,
queue: &mut VectorObjectQueue,
Expand Down
10 changes: 10 additions & 0 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
fn is_mmtk_object(&self, addr: Address) -> bool {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
// We don't need to search more than the max object size in the immix space.
let search_bytes = usize::min(super::MAX_IMMIX_OBJECT_SIZE, max_search_bytes);
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(ptr, search_bytes)
}
fn sft_trace_object(
&self,
_queue: &mut VectorObjectQueue,
Expand Down
11 changes: 11 additions & 0 deletions src/policy/immortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ impl<VM: VMBinding> SFT for ImmortalSpace<VM> {
fn is_mmtk_object(&self, addr: Address) -> bool {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}
fn sft_trace_object(
&self,
queue: &mut VectorObjectQueue,
Expand Down
39 changes: 39 additions & 0 deletions src/policy/largeobjectspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,45 @@ impl<VM: VMBinding> SFT for LargeObjectSpace<VM> {
fn is_mmtk_object(&self, addr: Address) -> bool {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
use crate::util::metadata::vo_bit;
// For large object space, it is a bit special. We only need to check VO bit for each page.
let mut cur_page = ptr.align_down(BYTES_IN_PAGE);
let low_page = ptr
.saturating_sub(max_search_bytes)
.align_down(BYTES_IN_PAGE);
while cur_page > low_page {
// If the page start is not mapped, there can't be an object in it.
if !cur_page.is_mapped() {
return None;
}
if vo_bit::get_raw_vo_bit_word(cur_page) != 0 {
// Find the exact address that has vo bit set
for offset in 0..vo_bit::VO_BIT_WORD_TO_REGION {
let addr = cur_page + offset;
if unsafe { vo_bit::is_vo_addr(addr) } {
let obj = vo_bit::is_internal_ptr_from_vo_bit::<VM>(addr, ptr);
if obj.is_some() {
return obj;
} else {
return None;
}
}
}
unreachable!(
"We found vo bit in the raw word, but we cannot find the exact address"
);
}

cur_page -= BYTES_IN_PAGE;
}
None
}
fn sft_trace_object(
&self,
queue: &mut VectorObjectQueue,
Expand Down
11 changes: 11 additions & 0 deletions src/policy/lockfreeimmortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ impl<VM: VMBinding> SFT for LockFreeImmortalSpace<VM> {
fn is_mmtk_object(&self, addr: Address) -> bool {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}
fn sft_trace_object(
&self,
_queue: &mut VectorObjectQueue,
Expand Down
12 changes: 12 additions & 0 deletions src/policy/markcompactspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ impl<VM: VMBinding> SFT for MarkCompactSpace<VM> {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}

#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}

fn sft_trace_object(
&self,
_queue: &mut VectorObjectQueue,
Expand Down
12 changes: 12 additions & 0 deletions src/policy/marksweepspace/malloc_ms/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ impl<VM: VMBinding> SFT for MallocSpace<VM> {
has_object_alloced_by_malloc::<VM>(addr).is_some()
}

#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}

fn initialize_object_metadata(&self, object: ObjectReference, _alloc: bool) {
trace!("initialize_object_metadata for object {}", object);
set_vo_bit::<VM>(object);
Expand Down
11 changes: 11 additions & 0 deletions src/policy/marksweepspace/native_ms/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ impl<VM: VMBinding> SFT for MarkSweepSpace<VM> {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}

#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
// We don't need to search more than the max object size in the mark sweep space.
let search_bytes = usize::min(MAX_OBJECT_SIZE, max_search_bytes);
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(ptr, search_bytes)
}

fn sft_trace_object(
&self,
queue: &mut VectorObjectQueue,
Expand Down
15 changes: 15 additions & 0 deletions src/policy/sft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ pub trait SFT {
#[cfg(feature = "is_mmtk_object")]
fn is_mmtk_object(&self, addr: Address) -> bool;

#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference>;

/// Initialize object metadata (in the header, or in the side metadata).
fn initialize_object_metadata(&self, object: ObjectReference, alloc: bool);

Expand Down Expand Up @@ -157,6 +164,14 @@ impl SFT for EmptySpaceSFT {
fn is_mmtk_object(&self, _addr: Address) -> bool {
false
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
_ptr: Address,
_max_search_bytes: usize,
) -> Option<ObjectReference> {
None
}

fn initialize_object_metadata(&self, object: ObjectReference, _alloc: bool) {
panic!(
Expand Down
11 changes: 11 additions & 0 deletions src/policy/vmspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ impl<VM: VMBinding> SFT for VMSpace<VM> {
fn is_mmtk_object(&self, addr: Address) -> bool {
crate::util::metadata::vo_bit::is_vo_bit_set_for_addr::<VM>(addr).is_some()
}
#[cfg(feature = "is_mmtk_object")]
fn find_object_from_internal_pointer(
&self,
ptr: Address,
max_search_bytes: usize,
) -> Option<ObjectReference> {
crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
ptr,
max_search_bytes,
)
}
fn sft_trace_object(
&self,
queue: &mut VectorObjectQueue,
Expand Down
Loading
Loading