Skip to content

Commit 6327179

Browse files
authored
memorypool: add optional support for the system allocator in Rc (#48305)
1 parent e604861 commit 6327179

File tree

2 files changed

+110
-10
lines changed

2 files changed

+110
-10
lines changed

benches/memorypool.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@ fn bench_memorypool_rc_new<const N: usize>(c: &mut Criterion, op_kcount: usize)
4343
});
4444
}
4545

46+
fn bench_system_rc_new<const N: usize>(c: &mut Criterion, op_kcount: usize) {
47+
let op_count = op_kcount * 1000;
48+
let bytes = mem::size_of::<[u64; N]>();
49+
let instances = RefCell::new(Vec::with_capacity(op_count));
50+
51+
c.bench_function(&format!("sys-rc-new-{bytes}b-x{op_kcount}k"), |b| {
52+
b.iter_batched_ref(
53+
|| instances.borrow_mut().clear(),
54+
|_| {
55+
let instances = &mut *instances.borrow_mut();
56+
let mut next_value: [u64; N] = [0; N];
57+
while next_value[0] < op_count as u64 {
58+
let n = memorypool::Rc::new(next_value);
59+
instances.push(n);
60+
next_value[0] += 1;
61+
}
62+
},
63+
BatchSize::PerIteration,
64+
)
65+
});
66+
}
67+
4668
fn bench_std_rc_new<const N: usize>(c: &mut Criterion, op_kcount: usize) {
4769
let op_count = op_kcount * 1000;
4870
let bytes = mem::size_of::<[u64; N]>();
@@ -88,6 +110,28 @@ fn bench_memorypool_rc_drop<const N: usize>(c: &mut Criterion, op_kcount: usize)
88110
});
89111
}
90112

113+
fn bench_system_rc_drop<const N: usize>(c: &mut Criterion, op_kcount: usize) {
114+
let op_count = op_kcount * 1000;
115+
let bytes = mem::size_of::<[u64; N]>();
116+
let instances = RefCell::new(Vec::with_capacity(op_count));
117+
118+
c.bench_function(&format!("sys-rc-drp-{bytes}b-x{op_kcount}k"), |b| {
119+
b.iter_batched_ref(
120+
|| {
121+
let instances = &mut *instances.borrow_mut();
122+
let mut next_value: [u64; N] = [0; N];
123+
while next_value[0] < op_count as u64 {
124+
let n = memorypool::Rc::new(next_value);
125+
instances.push(n);
126+
next_value[0] += 1;
127+
}
128+
},
129+
|_| instances.borrow_mut().clear(),
130+
BatchSize::PerIteration,
131+
)
132+
});
133+
}
134+
91135
fn bench_std_rc_drop<const N: usize>(c: &mut Criterion, op_kcount: usize) {
92136
let op_count = op_kcount * 1000;
93137
let bytes = mem::size_of::<[u64; N]>();
@@ -260,9 +304,11 @@ fn criterion_benchmark(c: &mut Criterion) {
260304
}
261305

262306
bench_memorypool_rc_new::<1>(c, OP_KCOUNT);
307+
bench_system_rc_new::<1>(c, OP_KCOUNT);
263308
bench_std_rc_new::<1>(c, OP_KCOUNT);
264309

265310
bench_memorypool_rc_drop::<1>(c, OP_KCOUNT);
311+
bench_system_rc_drop::<1>(c, OP_KCOUNT);
266312
bench_std_rc_drop::<1>(c, OP_KCOUNT);
267313

268314
bench_memorypool_rc_new::<80>(c, OP_KCOUNT);

src/core/memorypool.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ pub struct Rc<T> {
159159
}
160160

161161
impl<T> Rc<T> {
162+
pub fn new(v: T) -> Self {
163+
let ptr = Box::leak(Box::new(RcInner {
164+
refs: Cell::new(1),
165+
memory: Cell::new(None),
166+
key: Cell::new(0),
167+
value: v,
168+
}));
169+
170+
Self {
171+
ptr: ptr.into(),
172+
phantom: PhantomData,
173+
}
174+
}
175+
162176
pub fn try_new_in(v: T, memory: &std::rc::Rc<RcMemory<T>>) -> Result<Self, AllocError> {
163177
let (key, ptr) = memory
164178
.insert(RcInner {
@@ -194,16 +208,24 @@ impl<T> Rc<T> {
194208

195209
#[inline(never)]
196210
fn drop_slow(&mut self) {
197-
let key = self.inner().key.get();
198-
199-
// Entries contain a std::rc::Rc to the Memory they are contained in,
200-
// and we need to be careful the Memory is not dropped while an entry
201-
// is being removed. To ensure this, the Rc is moved out of the entry
202-
// and dropped only after the entry is removed.
203-
204-
let memory = self.inner().memory.take().unwrap();
205-
206-
memory.remove(key);
211+
if let Some(memory) = self.inner().memory.take() {
212+
// Slab allocations contain a std::rc::Rc to the Memory they are
213+
// contained in, and we need to be careful the Memory is not
214+
// dropped while an entry is being removed. To ensure this, the
215+
// Rc is moved out of the entry above, and it is dropped only
216+
// after the entry is removed.
217+
218+
let key = self.inner().key.get();
219+
220+
memory.remove(key);
221+
} else {
222+
// If there is no reference to slab memory, then the memory is
223+
// managed by the system allocator. To free it, we simply convert
224+
// the pointer back to a Box and drop it.
225+
226+
// SAFETY: While this Rc is alive, ptr is always valid
227+
unsafe { drop(Box::from_raw(self.ptr.as_mut())) };
228+
}
207229
}
208230
}
209231

@@ -333,8 +355,40 @@ impl ReusableVec {
333355
mod tests {
334356
use super::*;
335357

358+
struct FlagOnDrop {
359+
dropped: std::rc::Rc<Cell<bool>>,
360+
}
361+
362+
impl Drop for FlagOnDrop {
363+
fn drop(&mut self) {
364+
self.dropped.set(true);
365+
}
366+
}
367+
336368
#[test]
337369
fn test_rc() {
370+
let dropped = std::rc::Rc::new(Cell::new(false));
371+
372+
let f = FlagOnDrop {
373+
dropped: std::rc::Rc::clone(&dropped),
374+
};
375+
376+
let a = Rc::new(f);
377+
assert_eq!(a.inner().refs.get(), 1);
378+
379+
let b = Rc::clone(&a);
380+
assert_eq!(a.inner().refs.get(), 2);
381+
382+
drop(a);
383+
assert_eq!(b.inner().refs.get(), 1);
384+
assert!(!dropped.get());
385+
386+
drop(b);
387+
assert!(dropped.get());
388+
}
389+
390+
#[test]
391+
fn test_rc_in() {
338392
let memory = std::rc::Rc::new(RcMemory::new(2));
339393
assert_eq!(memory.len(), 0);
340394

0 commit comments

Comments
 (0)