Skip to content

Commit

Permalink
first integration of SegmentCache
Browse files Browse the repository at this point in the history
* Improves 'reserve()' again while at it.
* only 'push_cached()' for testing
* add cached benchmarks

We are getting closer to std Vec speed!
  • Loading branch information
cehteh committed Jun 10, 2023
1 parent 5c6e6cd commit 0ffdce4
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 14 deletions.
37 changes: 35 additions & 2 deletions benches/segvec_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
use segvec::*;

pub fn criterion_benchmark(c: &mut Criterion) {
//const N: usize = 10000;
//const N: i32 = 10000;
const N: i32 = 1000000;
c.bench_function("push values on a std Vec", |b| {
b.iter_with_large_drop(|| {
Expand Down Expand Up @@ -51,7 +51,40 @@ pub fn criterion_benchmark(c: &mut Criterion) {
v.push(black_box(i));
}
});
});
})
.bench_function("push values with linear growth, factor 32, cached", |b| {
b.iter_with_large_drop(|| {
let mut v: SegVec<i32, Linear<32>> = SegVec::with_capacity(0);
let mut cache = v.new_cache();
for i in 0..N {
v.push_cached(black_box(i), &mut cache);
}
});
})
.bench_function(
"push values with proportional growth, factor 32, cached",
|b| {
b.iter_with_large_drop(|| {
let mut v: SegVec<i32, Proportional<32>> = SegVec::with_capacity(0);
let mut cache = v.new_cache();
for i in 0..N {
v.push_cached(black_box(i), &mut cache);
}
})
},
)
.bench_function(
"push values with exponential growth, factor 32, cached",
|b| {
b.iter_with_large_drop(|| {
let mut v: SegVec<i32, Exponential<32>> = SegVec::with_capacity(0);
let mut cache = v.new_cache();
for i in 0..N {
v.push_cached(black_box(i), &mut cache);
}
});
},
);
}

criterion_group!(benches, criterion_benchmark);
Expand Down
38 changes: 33 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ impl<T, C: MemConfig> SegVec<T, C> {
v
}

/// Returns a [`SegmentCache`] suitable to be used with this `SegVec`.
#[inline]
pub fn new_cache(&self) -> SegmentCache<C> {
SegmentCache::<C>::new()
}

/// The number of elements in the [`SegVec`][crate::SegVec]
///
/// ```
Expand Down Expand Up @@ -183,16 +189,31 @@ impl<T, C: MemConfig> SegVec<T, C> {
None => capacity_overflow(),
};
if min_cap > self.capacity {
self.reserve_cold(min_cap);
self.reserve_cold(min_cap, None);
}
}

#[inline]
pub fn reserve_cached(&mut self, additional: usize, cache: &mut SegmentCache<C>) {
let min_cap = match self.len().checked_add(additional) {
Some(c) => c,
None => capacity_overflow(),
};
if min_cap > self.capacity {
self.reserve_cold(min_cap, Some(cache));
}
}

// do the real reserving in a cold path
#[cold]
fn reserve_cold(&mut self, min_cap: usize) {
let (segment, _) = C::segment_and_offset(min_cap - 1);
fn reserve_cold(&mut self, min_cap: usize, mut cache: Option<&mut SegmentCache<C>>) {
let segment = cache
.as_mut()
.map_or_else(|| C::segment(min_cap - 1), |c| c.segment(min_cap - 1));
for i in self.segments.len()..=segment {
let seg_size = C::segment_size(i);
let seg_size = cache
.as_ref()
.map_or_else(|| C::segment_size(i), |c| c.segment_size(i));
self.segments.push(detail::Segment::with_capacity(seg_size));
}
self.capacity = self.capacity();
Expand Down Expand Up @@ -249,7 +270,14 @@ impl<T, C: MemConfig> SegVec<T, C> {
/// - If the required capacity overflows `usize`
pub fn push(&mut self, val: T) {
self.reserve(1);
let (seg, _) = C::segment_and_offset(self.len);
let seg = C::segment(self.len);
self.segments[seg].push(val);
self.len += 1;
}

pub fn push_cached(&mut self, val: T, cache: &mut SegmentCache<C>) {
self.reserve_cached(1, cache);
let seg = cache.segment(self.len);
self.segments[seg].push(val);
self.len += 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/mem_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub trait MemConfig {

/// Gets the segment from a flat index
fn segment(index: usize) -> usize {
let (s, i) = Self::segment_and_offset(index);
let (s, _) = Self::segment_and_offset(index);
s
}

Expand Down
7 changes: 1 addition & 6 deletions src/segment_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,8 @@ impl<C: MemConfig> SegmentCache<C> {
if (self.start..self.end).contains(&index) {
// cache hit
self.segment
} else if index >= self.end && index < self.end + C::factor() {
// in next segment
self.start = self.end;
self.segment += 1;
self.end = self.start + C::segment_size(self.segment);
self.segment
} else {
// cache miss
self.segment_cold(index)
}
}
Expand Down

0 comments on commit 0ffdce4

Please sign in to comment.