Skip to content

Commit 36b58ce

Browse files
committed
use bitsets
1 parent c0fbaea commit 36b58ce

File tree

1 file changed

+32
-18
lines changed

1 file changed

+32
-18
lines changed

src/alloc/isolated_alloc.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ use std::collections::HashMap;
33
use std::hash::{BuildHasherDefault, DefaultHasher};
44
use std::sync;
55

6+
use rustc_index::bit_set::DenseBitSet;
7+
68
static ALLOCATOR: sync::Mutex<IsolatedAlloc> = sync::Mutex::new(IsolatedAlloc::empty());
79

810
pub struct IsolatedAlloc {
11+
/// A map of machine ID to allocator. If running in multi-seeded mode,
12+
/// each machine should have its own pool of memory that can be accessed
13+
/// separately. We use the normal `HashMap` type so that it's available
14+
/// in a `const` context.
915
#[allow(rustc::default_hash_types)]
1016
allocators: HashMap<u64, IsolatedAllocInner, BuildHasherDefault<DefaultHasher>>,
1117
/// The host (not emulated) page size, or 0 if it has not yet been set.
@@ -24,14 +30,14 @@ pub struct IsolatedAllocInner {
2430
/// with their size stored as the second element of the vector.
2531
huge_ptrs: Vec<(*mut u8, usize)>,
2632
/// Metadata about which bytes have been allocated on each page. The length
27-
/// of this vector must be the same as that of `page_ptrs`, and the length of
28-
/// the boxed slice must be exactly `page_size / 8`.
33+
/// of this vector must be the same as that of `page_ptrs`, and the domain
34+
/// size of the bitset must be exactly `page_size / 8`.
2935
///
30-
/// Conceptually, each bit of the `u8` represents the allocation status of one
31-
/// byte on the corresponding element of `page_ptrs`; in practice, we only allocate
32-
/// in 8-byte chunks currently, so the `u8`s are only ever 0 (fully free) or
33-
/// 255 (fully allocated).
34-
page_infos: Vec<Box<[u8]>>,
36+
/// Conceptually, each bit of the bitset represents the allocation status of
37+
/// one 8-byte chunk on the corresponding element of `page_ptrs`. Thus,
38+
/// indexing into it should be done with a value one-eighth of the corresponding
39+
/// offset on the matching `page_ptrs` element.
40+
page_infos: Vec<DenseBitSet<usize>>,
3541
}
3642

3743
// SAFETY: We only point to heap-allocated data
@@ -42,6 +48,7 @@ impl IsolatedAlloc {
4248
/// allow this function to be `const`; it is updated to its real value on
4349
/// the first call to `alloc()` or `alloc_zeroed()`.
4450
const fn empty() -> Self {
51+
// We need this to be `const`
4552
#[allow(rustc::default_hash_types)]
4653
Self { allocators: HashMap::with_hasher(BuildHasherDefault::new()), page_size: 0 }
4754
}
@@ -108,14 +115,15 @@ impl IsolatedAllocInner {
108115
}
109116

110117
/// Expands the available memory pool by adding one page.
111-
fn add_page(&mut self, page_size: usize) -> (*mut u8, &mut Box<[u8]>) {
118+
fn add_page(&mut self, page_size: usize) -> (*mut u8, &mut DenseBitSet<usize>) {
112119
assert_ne!(page_size, 0);
113120

114121
let page_layout = unsafe { Layout::from_size_align_unchecked(page_size, page_size) };
115122
// We don't overwrite the bytes we hand out so make sure they're zeroed by default!
116123
let page_ptr = unsafe { alloc::alloc(page_layout) };
117-
// `page_infos` has to be one-eighth of the pagesize per the field docs
118-
self.page_infos.push(vec![0u8; page_size / 8].into_boxed_slice());
124+
// `page_infos` has to have one-eighth as many bits as a page has bytes
125+
// (or one-64th as many bytes)
126+
self.page_infos.push(DenseBitSet::new_empty(page_size / 8));
119127
self.page_ptrs.push(page_ptr);
120128
(page_ptr, self.page_infos.last_mut().unwrap())
121129
}
@@ -160,23 +168,28 @@ impl IsolatedAllocInner {
160168
page_size: usize,
161169
layout: Layout,
162170
page: *mut u8,
163-
pinfo: &mut Box<[u8]>,
171+
pinfo: &mut DenseBitSet<usize>,
164172
zeroed: bool,
165173
) -> Option<*mut u8> {
166174
let (size, align) = IsolatedAllocInner::normalized_layout(layout);
167175

176+
// Check every alignment-sized block and see if there exists a `size`
177+
// chunk of empty space i.e. forall idx . !pinfo.contains(idx / 8)
168178
for idx in (0..page_size).step_by(align) {
169179
let idx_pinfo = idx / 8;
170180
let size_pinfo = size / 8;
171-
if pinfo.len() < idx_pinfo + size_pinfo {
181+
// DenseBitSet::contains() panics if the index is out of bounds
182+
if pinfo.domain_size() < idx_pinfo + size_pinfo {
172183
break;
173184
}
174-
if pinfo[idx_pinfo..idx_pinfo + size_pinfo].iter().all(|v| *v == 0) {
175-
pinfo[idx_pinfo..idx_pinfo + size_pinfo].fill(255);
185+
let pred = !(idx_pinfo..idx_pinfo + size_pinfo).any(|idx| pinfo.contains(idx));
186+
if pred {
187+
pinfo.insert_range(idx_pinfo..idx_pinfo + size_pinfo);
176188
unsafe {
177189
let ptr = page.add(idx);
178190
if zeroed {
179-
// Only write the bytes we were specifically asked to zero out
191+
// Only write the bytes we were specifically asked to
192+
// zero out, even if we allocated more
180193
ptr.write_bytes(0, layout.size());
181194
}
182195
return Some(ptr);
@@ -223,14 +236,15 @@ impl IsolatedAllocInner {
223236
};
224237
let ptr_idx_pinfo = ptr_idx / 8;
225238
let size_pinfo = size / 8;
226-
// Everything is always aligned to at least 8 bytes so this is ok
227-
pinfo[ptr_idx_pinfo..ptr_idx_pinfo + size_pinfo].fill(0);
239+
for idx in ptr_idx_pinfo..ptr_idx_pinfo + size_pinfo {
240+
pinfo.remove(idx);
241+
}
228242
}
229243

230244
let mut free = vec![];
231245
let page_layout = unsafe { Layout::from_size_align_unchecked(page_size, page_size) };
232246
for (idx, pinfo) in self.page_infos.iter().enumerate() {
233-
if pinfo.iter().all(|p| *p == 0) {
247+
if pinfo.is_empty() {
234248
free.push(idx);
235249
}
236250
}

0 commit comments

Comments
 (0)