Skip to content

Commit 31add7e

Browse files
committed
Auto merge of #71872 - nnethercote:less-aggressive-arena-growth, r=oli-obk
Be less aggressive with `DroplessArena`/`TypedArena` growth. `DroplessArena` and `TypedArena` use an aggressive growth strategy: the first chunk is 4 KiB, the second is 8 KiB, and it keeps on doubling indefinitely. DHAT profiles show that sometimes this results in large chunks (e.g. 16-128 MiB) that are barely filled. This commit changes things so that the doubling stops at 2 MiB. This is large enough that chunk allocations are still rare (you might get 100s instead of 10s of them) but avoids lots of unused space in the worst case. It makes the same change to `TypedArena`, too.
2 parents 584252b + 40d4868 commit 31add7e

File tree

1 file changed

+26
-13
lines changed

1 file changed

+26
-13
lines changed

src/libarena/lib.rs

+26-13
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
//! of individual objects while the arena itself is still alive. The benefit
66
//! of an arena is very fast allocation; just a pointer bump.
77
//!
8-
//! This crate implements `TypedArena`, a simple arena that can only hold
9-
//! objects of a single type.
8+
//! This crate implements several kinds of arena.
109
1110
#![doc(
1211
html_root_url = "https://doc.rust-lang.org/nightly/",
@@ -98,7 +97,13 @@ impl<T> TypedArenaChunk<T> {
9897
}
9998
}
10099

100+
// The arenas start with PAGE-sized chunks, and then each new chunk is twice as
101+
// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon
102+
// we stop growing. This scales well, from arenas that are barely used up to
103+
// arenas that are used for 100s of MiBs. Note also that the chosen sizes match
104+
// the usual sizes of pages and huge pages on Linux.
101105
const PAGE: usize = 4096;
106+
const HUGE_PAGE: usize = 2 * 1024 * 1024;
102107

103108
impl<T> Default for TypedArena<T> {
104109
/// Creates a new `TypedArena`.
@@ -211,6 +216,9 @@ impl<T> TypedArena<T> {
211216
#[cold]
212217
fn grow(&self, n: usize) {
213218
unsafe {
219+
// We need the element size in to convert chunk sizes (ranging from
220+
// PAGE to HUGE_PAGE bytes) to element counts.
221+
let elem_size = cmp::max(1, mem::size_of::<T>());
214222
let mut chunks = self.chunks.borrow_mut();
215223
let (chunk, mut new_capacity);
216224
if let Some(last_chunk) = chunks.last_mut() {
@@ -221,18 +229,20 @@ impl<T> TypedArena<T> {
221229
self.end.set(last_chunk.end());
222230
return;
223231
} else {
232+
// If the previous chunk's capacity is less than HUGE_PAGE
233+
// bytes, then this chunk will be least double the previous
234+
// chunk's size.
224235
new_capacity = last_chunk.storage.capacity();
225-
loop {
236+
if new_capacity < HUGE_PAGE / elem_size {
226237
new_capacity = new_capacity.checked_mul(2).unwrap();
227-
if new_capacity >= currently_used_cap + n {
228-
break;
229-
}
230238
}
231239
}
232240
} else {
233-
let elem_size = cmp::max(1, mem::size_of::<T>());
234-
new_capacity = cmp::max(n, PAGE / elem_size);
241+
new_capacity = PAGE / elem_size;
235242
}
243+
// Also ensure that this chunk can fit `n`.
244+
new_capacity = cmp::max(n, new_capacity);
245+
236246
chunk = TypedArenaChunk::<T>::new(new_capacity);
237247
self.ptr.set(chunk.start());
238248
self.end.set(chunk.end());
@@ -347,17 +357,20 @@ impl DroplessArena {
347357
self.end.set(last_chunk.end());
348358
return;
349359
} else {
360+
// If the previous chunk's capacity is less than HUGE_PAGE
361+
// bytes, then this chunk will be least double the previous
362+
// chunk's size.
350363
new_capacity = last_chunk.storage.capacity();
351-
loop {
364+
if new_capacity < HUGE_PAGE {
352365
new_capacity = new_capacity.checked_mul(2).unwrap();
353-
if new_capacity >= used_bytes + needed_bytes {
354-
break;
355-
}
356366
}
357367
}
358368
} else {
359-
new_capacity = cmp::max(needed_bytes, PAGE);
369+
new_capacity = PAGE;
360370
}
371+
// Also ensure that this chunk can fit `needed_bytes`.
372+
new_capacity = cmp::max(needed_bytes, new_capacity);
373+
361374
chunk = TypedArenaChunk::<u8>::new(new_capacity);
362375
self.ptr.set(chunk.start());
363376
self.end.set(chunk.end());

0 commit comments

Comments
 (0)