Skip to content

Commit 94808d5

Browse files
author
TechnoPorg
committed
Faster conversions to doubling vectors
1 parent c51f12b commit 94808d5

5 files changed

Lines changed: 120 additions & 20 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ orx-concurrent-iter = { version = "3.1.0", default-features = false }
1919
name = "serial_access"
2020
harness = false
2121

22+
[[bench]]
23+
name = "from"
24+
harness = false
25+
2226
[dev-dependencies]
2327
criterion = "0.7.0"
2428
rand = { version = "0.9.2", default-features = false }

benches/from.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use criterion::{
2+
BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main, measurement::WallTime,
3+
};
4+
use orx_split_vec::{Doubling, Linear, Recursive, SplitVec};
5+
use std::hint::black_box;
6+
7+
fn get_vec(len: usize) -> Vec<u64> {
8+
let modulo = len % 3;
9+
if modulo == 0 {
10+
vec![len as u64; len]
11+
} else if modulo == 1 {
12+
vec![(len + 1) as u64; len]
13+
} else {
14+
vec![(len + 2) as u64; len]
15+
}
16+
}
17+
18+
fn doubling_from_std_vec<T>(std_vec: Vec<T>) -> SplitVec<T, Doubling> {
19+
SplitVec::from(std_vec)
20+
}
21+
22+
fn linear_from_std_vec<T>(std_vec: Vec<T>) -> SplitVec<T, Linear> {
23+
SplitVec::from(std_vec)
24+
}
25+
26+
fn recursive_from_std_vec<T>(
27+
std_vec: Vec<T>
28+
) -> SplitVec<T, Recursive> {
29+
SplitVec::from(std_vec)
30+
}
31+
32+
fn test_for_type<T>(
33+
group: &mut BenchmarkGroup<'_, WallTime>,
34+
num_u64s: usize,
35+
treatments: &[usize],
36+
value: fn(usize) -> Vec<T>,
37+
) {
38+
for n in treatments {
39+
let treatment = format!("n={},elem-type=[u64;{}]", n, num_u64s);
40+
41+
group.bench_with_input(
42+
BenchmarkId::new("doubling_from_std_vec", &treatment),
43+
n,
44+
|b, _| b.iter(|| doubling_from_std_vec(value(black_box(*n)))),
45+
);
46+
47+
group.bench_with_input(
48+
BenchmarkId::new("linear_from_std_vec", &treatment),
49+
n,
50+
|b, _| b.iter(|| linear_from_std_vec(value(black_box(*n)))),
51+
);
52+
group.bench_with_input(
53+
BenchmarkId::new("recursive_from_std_vec", &treatment),
54+
n,
55+
|b, _| b.iter(|| recursive_from_std_vec(value(black_box(*n))),
56+
));
57+
}
58+
}
59+
60+
fn bench_from(c: &mut Criterion) {
61+
let treatments = vec![1_024, 16_384, 262_144, 4_194_304];
62+
63+
let mut group = c.benchmark_group("from");
64+
65+
const N: usize = 16;
66+
67+
test_for_type(&mut group, N, &treatments, get_vec);
68+
69+
group.finish();
70+
}
71+
72+
criterion_group!(benches, bench_from);
73+
criterion_main!(benches);

src/growth/doubling/from.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use core::{cmp::min, mem::MaybeUninit, ptr::copy_nonoverlapping};
2+
13
use super::constants::CUMULATIVE_CAPACITIES;
2-
use crate::{Doubling, Fragment, SplitVec};
4+
use crate::{Doubling, Fragment, SplitVec, growth::doubling::constants::CAPACITIES};
35
use alloc::vec::Vec;
46

5-
impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
7+
impl<T> From<Vec<T>> for SplitVec<T, Doubling> {
68
/// Converts a `Vec` into a `SplitVec`.
79
///
810
/// # Examples
@@ -19,8 +21,9 @@ impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
1921
/// assert_eq!(1, split_vec.fragments().len());
2022
/// assert!(vec_capacity <= split_vec.capacity());
2123
/// ```
22-
fn from(value: Vec<T>) -> Self {
24+
fn from(mut value: Vec<T>) -> Self {
2325
let len = value.len();
26+
// Number of fragments to create
2427
let f = CUMULATIVE_CAPACITIES
2528
.iter()
2629
.enumerate()
@@ -29,26 +32,33 @@ impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
2932
.expect("overflow");
3033

3134
let mut fragments = Vec::with_capacity(f + 1);
32-
let mut original_idx = 0;
35+
let fragments_init = fragments.spare_capacity_mut();
3336
let mut remaining_len = len;
34-
let mut curr_f = 1;
37+
let mut curr_f = f;
3538
while remaining_len > 0 {
36-
let capacity = &CUMULATIVE_CAPACITIES[curr_f];
37-
let mut fragment = Fragment::new(*capacity);
38-
39-
let copy_len = if capacity <= &remaining_len {
40-
*capacity
41-
} else {
42-
remaining_len
43-
};
44-
45-
fragment.extend_from_slice(&value[original_idx..(original_idx + copy_len)]);
46-
47-
original_idx += copy_len;
39+
curr_f -= 1;
40+
let capacity = CAPACITIES[curr_f];
41+
// for example, if the current fragment has a capacity of 8 but there are only 5 elements to copy,
42+
// we want the copy length to only be 1
43+
let copy_len = min(remaining_len - CUMULATIVE_CAPACITIES[curr_f], capacity);
4844
remaining_len -= copy_len;
49-
fragments.push(fragment);
50-
curr_f += 1;
45+
46+
// This is adapted from Vec::split_off, with the difference that it
47+
// reserves the full capacity first to avoid extra allocations
48+
let mut fragment_data = Vec::with_capacity(capacity);
49+
unsafe {
50+
value.set_len(remaining_len);
51+
fragment_data.set_len(copy_len);
52+
copy_nonoverlapping(
53+
value.as_ptr().add(remaining_len),
54+
fragment_data.as_mut_ptr(),
55+
copy_len,
56+
);
57+
}
58+
fragments_init[curr_f] = MaybeUninit::new(Fragment::from(fragment_data));
5159
}
60+
debug_assert_eq!(curr_f, 0);
61+
unsafe { fragments.set_len(f) };
5262

5363
Self::from_raw_parts(len, fragments, Doubling)
5464
}

src/growth/doubling/tests/from.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,16 @@ fn from_vec_medium() {
1414
validate_clone(vec, split_vec);
1515
}
1616
}
17+
18+
#[test]
19+
fn from_same_as_push() {
20+
for len in 0..135 {
21+
let vec: Vec<_> = (0..len).collect();
22+
let split_vec_from: SplitVec<_, Doubling> = vec.clone().into();
23+
let mut split_vec_manual = SplitVec::new();
24+
for item in vec {
25+
split_vec_manual.push(item);
26+
}
27+
assert_eq!(split_vec_from, split_vec_manual);
28+
}
29+
}

src/growth/recursive/from.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<T> From<SplitVec<T, Linear>> for SplitVec<T, Recursive> {
5555
}
5656
}
5757

58-
impl<T: Clone> From<Vec<T>> for SplitVec<T, Recursive> {
58+
impl<T> From<Vec<T>> for SplitVec<T, Recursive> {
5959
/// Converts a `Vec` into a `SplitVec`.
6060
///
6161
/// # Examples

0 commit comments

Comments
 (0)