Skip to content

Commit f177258

Browse files
authored
Replace FragmentedMapper with TwoLevelMmapper (#1337)
This PR replaces `FragmentedMapper` with `TwoLevelMmapper`. `TwoLevelMmapper` does not use hash tables. It is also lock-free when looking up `MapState` entries, and also lock-free when lazily initializing slabs. It is intended to eliminate the pathological case of `FragmentedMapper` where a hash collision can result in a long-distance linear scan and many locking operations. Fixes: #1336
1 parent 1dc6815 commit f177258

File tree

6 files changed

+283
-210
lines changed

6 files changed

+283
-210
lines changed

benches/mock_bench/mmapper.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
pub use criterion::Criterion;
2+
3+
use mmtk::{
4+
memory_manager,
5+
util::{test_util::fixtures::*, Address},
6+
};
7+
8+
pub fn bench(c: &mut Criterion) {
9+
let mut fixture = MutatorFixture::create_with_heapsize(1 << 30);
10+
11+
let regular = memory_manager::alloc(
12+
&mut fixture.mutator,
13+
40,
14+
0,
15+
0,
16+
mmtk::AllocationSemantics::Default,
17+
);
18+
19+
let large = memory_manager::alloc(
20+
&mut fixture.mutator,
21+
40,
22+
0,
23+
0,
24+
mmtk::AllocationSemantics::Los,
25+
);
26+
27+
let low = unsafe { Address::from_usize(42usize) };
28+
let high = unsafe { Address::from_usize(usize::MAX - 1024usize) };
29+
30+
c.bench_function("is_mapped_regular", |b| {
31+
b.iter(|| {
32+
let is_mapped = regular.is_mapped();
33+
assert!(is_mapped);
34+
})
35+
});
36+
37+
c.bench_function("is_mapped_large", |b| {
38+
b.iter(|| {
39+
let is_mapped = large.is_mapped();
40+
assert!(is_mapped);
41+
})
42+
});
43+
44+
c.bench_function("is_mapped_low", |b| {
45+
b.iter(|| {
46+
let is_mapped = low.is_mapped();
47+
assert!(!is_mapped);
48+
})
49+
});
50+
51+
c.bench_function("is_mapped_high", |b| {
52+
b.iter(|| {
53+
let is_mapped = high.is_mapped();
54+
assert!(!is_mapped);
55+
})
56+
});
57+
58+
// The following bench involves large address ranges and cannot run on 32-bit machines.
59+
#[cfg(target_pointer_width = "64")]
60+
c.bench_function("is_mapped_seq", |b| {
61+
b.iter(|| {
62+
use mmtk::util::heap::vm_layout::BYTES_IN_CHUNK;
63+
let start = regular.as_usize();
64+
let num_chunks = 16384usize;
65+
let end = start + num_chunks * BYTES_IN_CHUNK;
66+
for addr_usize in (start..end).step_by(BYTES_IN_CHUNK) {
67+
let addr = unsafe { Address::from_usize(addr_usize) };
68+
let _is_mapped = addr.is_mapped();
69+
}
70+
})
71+
});
72+
}

benches/mock_bench/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use criterion::Criterion;
22

33
pub mod alloc;
44
pub mod internal_pointer;
5+
pub mod mmapper;
56
pub mod sft;
67

78
// As we can only initialize one MMTk instance, we have to run each benchmark in a separate process.
@@ -26,6 +27,7 @@ pub fn bench(c: &mut Criterion) {
2627
Ok(bench) => match bench.as_str() {
2728
"alloc" => alloc::bench(c),
2829
"internal_pointer" => internal_pointer::bench(c),
30+
"mmapper" => mmapper::bench(c),
2931
"sft" => sft::bench(c),
3032
_ => panic!("Unknown benchmark {:?}", bench),
3133
},

src/util/heap/layout/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod mmapper;
55
pub use self::mmapper::Mmapper;
66
mod byte_map_mmapper;
77
#[cfg(target_pointer_width = "64")]
8-
mod fragmented_mapper;
8+
mod two_level_mmapper;
99

1010
mod map;
1111
pub(crate) use self::map::CreateFreeListResult;
@@ -37,7 +37,7 @@ pub fn create_mmapper() -> Box<dyn Mmapper + Send + Sync> {
3737
#[cfg(target_pointer_width = "64")]
3838
pub fn create_mmapper() -> Box<dyn Mmapper + Send + Sync> {
3939
// TODO: ByteMapMmapper for 39-bit or less virtual space
40-
Box::new(fragmented_mapper::FragmentedMapper::new())
40+
Box::new(two_level_mmapper::TwoLevelMmapper::new())
4141
}
4242

4343
use crate::util::Address;

0 commit comments

Comments
 (0)