Skip to content

Commit 18a82aa

Browse files
committed
use alloc_excess and realloc_excess
1 parent 3639c16 commit 18a82aa

File tree

8 files changed

+45
-62
lines changed

8 files changed

+45
-62
lines changed

src/doc/book

Submodule book updated 46 files

src/doc/nomicon

src/liballoc/raw_vec.rs

Lines changed: 38 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::mem;
1313
use core::ops::Drop;
1414
use core::ptr::{self, Unique};
1515
use core::slice;
16-
use heap::{Alloc, Layout, Heap};
16+
use heap::{Alloc, Layout, Heap, Excess};
1717
use super::boxed::Box;
1818

1919
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
@@ -174,29 +174,29 @@ impl<T, A: Alloc> RawVec<T, A> {
174174
/// Panics on overflow if `current_capacity + capacity_increase >
175175
/// std::usize::MAX`.
176176
///
177-
///
178177
/// # Growth strategy
179178
///
180179
/// RawVec grows differently depending on:
181180
///
182-
/// - 1. initial size: grows from zero to at least 64 bytes;
181+
/// - 1. initial size: grows from zero to four elements or at least 64 bytes;
183182
/// use `with_capacity` to avoid a growth from zero.
184183
///
185184
/// - 2. vector size:
186-
/// - small vectors (<= 4096 bytes) and large vectors (>= 4096 * 32 bytes)
187-
/// grow with a growth factor of 2x.
188-
/// - otherwise (medium-sized vectors) grow with a growth factor of 1.5x.
185+
/// - small vectors (<= 4096 bytes) grow with a factor of 2x.
186+
/// - medium-sized and large vectors grow with a growth factor of 1.5x.
189187
///
190188
/// # Growth factor
191189
///
192-
/// Medium-sized vectors' growth-factor is chosen to allow reusing memory from
193-
/// previous allocations. Previously freed memory can be reused after
190+
/// The growth factor of medium-sized and large vectors is chosen to allow
191+
/// reusing memory from previously-freed allocations in subsequent allocations.
192+
///
193+
/// Depending on the growth factor, previously freed memory can be reused after
194194
///
195-
/// - 4 reallocations for a growth factor of 1.5x
196-
/// - 3 reallocations for a growth factor of 1.45x
197-
/// - 2 reallocations for a growth factor of 1.3x
195+
/// - 4 reallocations for a growth factor of 1.5x,
196+
/// - 3 reallocations for a growth factor of 1.45x,
197+
/// - 2 reallocations for a growth factor of 1.3x,
198198
///
199-
/// Which one is better [is application
199+
/// Which growth-factor is better [is application
200200
/// dependent](https://stackoverflow.com/questions/1100311/
201201
/// what-is-the-ideal-growth-rate-for-a-dynamically-allocated-array),
202202
/// also some claim that [the golden ration (1.618) is
@@ -205,7 +205,12 @@ impl<T, A: Alloc> RawVec<T, A> {
205205
/// The trade-off is having to wait for many reallocations to be able to
206206
/// reuse old memory.
207207
///
208-
/// Note: a factor of 2x _never_ allows reusing previously-freed memory.
208+
/// A factor of 2x _never_ allows reusing any previously-freed memory but
209+
/// `jemalloc`'s memory blocks < 4096 bytes cannot grow in place, that is,
210+
/// trying to grow a vector < 4096 bytes is always going to require allocating
211+
/// new memory and copying the contents over. Since `jemalloc`'s memory pools
212+
/// for small allocations grow with powers of 2 it makes sense to keep a
213+
/// growth-factor of 2x for these allocations.
209214
///
210215
#[inline(always)]
211216
fn amortized_new_capacity(elem_size: usize, current_capacity: usize,
@@ -215,31 +220,18 @@ fn amortized_new_capacity(elem_size: usize, current_capacity: usize,
215220
// `current_capacity <= isize::MAX` so that `current_capacity * N` where `N
216221
// <= 2` cannot overflow.
217222
let growth_capacity = match current_capacity {
218-
// Empty vector => at least 64 bytes
219-
//
220-
// [Original]:
221-
0 => if elem_size > (!0) / 8 { 1 } else { 4 },
222-
// [NEW]:
223-
// 0 => (64 / elem_size).max(1),
224-
// [NEW 2]:
225-
// 0 => (32 / elem_size).max(4),
226-
//
227-
228-
// Small and large vectors (<= 4096 bytes, and >= 4096 * 32 bytes):
229-
//
230-
// FIXME: jemalloc specific behavior, allocators should provide a
231-
// way to query the byte size of blocks that can grow inplace.
232-
//
233-
// jemalloc can never grow in place small blocks but blocks larger
234-
// than or equal to 4096 bytes can be expanded in place:
235-
// c if c < 4096 / elem_size => 2 * c,
236-
// c if c > 4096 * 32 / elem_size => 2 * c,
237-
238-
// Medium sized vectors in the [4096, 4096 * 32) bytes range:
239-
c => c / 2 * 3 + 1,
240-
241-
// [Original]
242-
// c => 2 * c,
223+
// Empty vector => 4 elements or at least 64 bytes
224+
0 => (64 / elem_size).max(4),
225+
226+
// Small vectors: jemalloc cannot grow in place blocks smaller than 4096
227+
// bytes so until then the memory of previously-freed allocations
228+
// cannot be reused by subsequent allocations:
229+
c if c < 4096 / elem_size => 2 * c,
230+
231+
// Medium and large vectors (>= 4096 bytes): a growth factor of 1.5
232+
// allows the memory of a previously-freeed allocation to be reused
233+
// after 4 subsequent allocations:
234+
c => (c / 2 + 1) * 3,
243235
};
244236
cmp::max(growth_capacity,
245237
current_capacity.checked_add(capacity_increase).unwrap())
@@ -592,31 +584,25 @@ impl<T, A: Alloc> RawVec<T, A> {
592584
used_cap,
593585
needed_extra_cap);
594586

595-
let new_layout = match Layout::array::<T>(new_cap) {
596-
Some(layout) => layout,
597-
None => panic!("capacity overflow"),
598-
};
599-
let (_, usable_size) = self.a.usable_size(&new_layout);
600-
let new_cap = usable_size / elem_size;
601587
let new_layout = match Layout::array::<T>(new_cap) {
602588
Some(layout) => layout,
603589
None => panic!("capacity overflow"),
604590
};
605591
// FIXME: may crash and burn on over-reserve
606592
alloc_guard(new_layout.size());
607-
let res = match self.current_layout() {
593+
let Excess(res, usable_size) = match self.current_layout() {
608594
Some(layout) => {
609595
let old_ptr = self.ptr.as_ptr() as *mut u8;
610-
self.a.realloc(old_ptr, layout, new_layout)
596+
self.a.realloc_excess(old_ptr, layout, new_layout)
611597
}
612-
None => self.a.alloc(new_layout),
598+
None => self.a.alloc_excess(new_layout),
613599
};
614600
let uniq = match res {
615601
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
616602
Err(e) => self.a.oom(e),
617603
};
618604
self.ptr = uniq;
619-
self.cap = new_cap;
605+
self.cap = usable_size / mem::size_of::<T>();
620606
}
621607
}
622608

@@ -666,17 +652,14 @@ impl<T, A: Alloc> RawVec<T, A> {
666652

667653
let ptr = self.ptr() as *mut _;
668654
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
669-
let (_, usable_size) = self.a.usable_size(&new_layout);
670-
let new_cap = usable_size / elem_size;
671-
let new_layout = match Layout::array::<T>(new_cap) {
672-
Some(layout) => layout,
673-
None => panic!("capacity overflow"),
674-
};
675655
// FIXME: may crash and burn on over-reserve
676656
alloc_guard(new_layout.size());
677657
match self.a.grow_in_place(ptr, old_layout, new_layout) {
678658
Ok(_) => {
679-
self.cap = new_cap;
659+
// FIXME: grow_in_place swallows the usable_size, so we
660+
// need to recompute it here:
661+
let (_, new_usable_size) = self.a.usable_size(&new_layout);
662+
self.cap = new_usable_size / mem::size_of::<T>();
680663
true
681664
}
682665
Err(_) => {

src/liblibc

Submodule liblibc updated 63 files

src/tools/cargo

Submodule cargo updated 158 files

src/tools/clippy

src/tools/rls

Submodule rls updated from 9ad92e1 to 93b47d1

src/tools/rustfmt

0 commit comments

Comments
 (0)