Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ orx-concurrent-iter = { version = "3.1.0", default-features = false }
name = "serial_access"
harness = false

[[bench]]
name = "from"
harness = false

[dev-dependencies]
criterion = "0.7.0"
rand = { version = "0.9.2", default-features = false }
Expand Down
73 changes: 73 additions & 0 deletions benches/from.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use criterion::{
BenchmarkGroup, BenchmarkId, Criterion, criterion_group, criterion_main, measurement::WallTime,
};
use orx_split_vec::{Doubling, Linear, Recursive, SplitVec};
use std::hint::black_box;

fn get_vec(len: usize) -> Vec<u64> {
let modulo = len % 3;
if modulo == 0 {
vec![len as u64; len]
} else if modulo == 1 {
vec![(len + 1) as u64; len]
} else {
vec![(len + 2) as u64; len]
}
}

fn doubling_from_std_vec<T>(std_vec: Vec<T>) -> SplitVec<T, Doubling> {
SplitVec::from(std_vec)
}

fn linear_from_std_vec<T>(std_vec: Vec<T>) -> SplitVec<T, Linear> {
SplitVec::from(std_vec)
}

fn recursive_from_std_vec<T>(
std_vec: Vec<T>
) -> SplitVec<T, Recursive> {
SplitVec::from(std_vec)
}

fn test_for_type<T>(
group: &mut BenchmarkGroup<'_, WallTime>,
num_u64s: usize,
treatments: &[usize],
value: fn(usize) -> Vec<T>,
) {
for n in treatments {
let treatment = format!("n={},elem-type=[u64;{}]", n, num_u64s);

group.bench_with_input(
BenchmarkId::new("doubling_from_std_vec", &treatment),
n,
|b, _| b.iter(|| doubling_from_std_vec(value(black_box(*n)))),
);

group.bench_with_input(
BenchmarkId::new("linear_from_std_vec", &treatment),
n,
|b, _| b.iter(|| linear_from_std_vec(value(black_box(*n)))),
);
group.bench_with_input(
BenchmarkId::new("recursive_from_std_vec", &treatment),
n,
|b, _| b.iter(|| recursive_from_std_vec(value(black_box(*n))),
));
}
}

fn bench_from(c: &mut Criterion) {
let treatments = vec![1_024, 16_384, 262_144, 4_194_304];

let mut group = c.benchmark_group("from");

const N: usize = 16;

test_for_type(&mut group, N, &treatments, get_vec);

group.finish();
}

criterion_group!(benches, bench_from);
criterion_main!(benches);
48 changes: 29 additions & 19 deletions src/growth/doubling/from.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use core::{cmp::min, mem::MaybeUninit, ptr::copy_nonoverlapping};

use super::constants::CUMULATIVE_CAPACITIES;
use crate::{Doubling, Fragment, SplitVec};
use crate::{Doubling, Fragment, SplitVec, growth::doubling::constants::CAPACITIES};
use alloc::vec::Vec;

impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
impl<T> From<Vec<T>> for SplitVec<T, Doubling> {
/// Converts a `Vec` into a `SplitVec`.
///
/// # Examples
Expand All @@ -19,8 +21,9 @@ impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
/// assert_eq!(1, split_vec.fragments().len());
/// assert!(vec_capacity <= split_vec.capacity());
/// ```
fn from(value: Vec<T>) -> Self {
fn from(mut value: Vec<T>) -> Self {
let len = value.len();
// Number of fragments to create
let f = CUMULATIVE_CAPACITIES
.iter()
.enumerate()
Expand All @@ -29,26 +32,33 @@ impl<T: Clone> From<Vec<T>> for SplitVec<T, Doubling> {
.expect("overflow");

let mut fragments = Vec::with_capacity(f + 1);
let mut original_idx = 0;
let fragments_init = fragments.spare_capacity_mut();
let mut remaining_len = len;
let mut curr_f = 1;
let mut curr_f = f;
while remaining_len > 0 {
let capacity = &CUMULATIVE_CAPACITIES[curr_f];
let mut fragment = Fragment::new(*capacity);

let copy_len = if capacity <= &remaining_len {
*capacity
} else {
remaining_len
};

fragment.extend_from_slice(&value[original_idx..(original_idx + copy_len)]);

original_idx += copy_len;
curr_f -= 1;
let capacity = CAPACITIES[curr_f];
// for example, if the current fragment has a capacity of 8 but there are only 5 elements to copy,
// we want the copy length to only be 1
let copy_len = min(remaining_len - CUMULATIVE_CAPACITIES[curr_f], capacity);
remaining_len -= copy_len;
fragments.push(fragment);
curr_f += 1;

// This is adapted from Vec::split_off, with the difference that it
// reserves the full capacity first to avoid extra allocations
let mut fragment_data = Vec::with_capacity(capacity);
unsafe {
value.set_len(remaining_len);
fragment_data.set_len(copy_len);
copy_nonoverlapping(
value.as_ptr().add(remaining_len),
fragment_data.as_mut_ptr(),
copy_len,
);
}
fragments_init[curr_f] = MaybeUninit::new(Fragment::from(fragment_data));
}
debug_assert_eq!(curr_f, 0);
unsafe { fragments.set_len(f) };

Self::from_raw_parts(len, fragments, Doubling)
}
Expand Down
13 changes: 13 additions & 0 deletions src/growth/doubling/tests/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,16 @@ fn from_vec_medium() {
validate_clone(vec, split_vec);
}
}

#[test]
fn from_same_as_push() {
for len in 0..135 {
let vec: Vec<_> = (0..len).collect();
let split_vec_from: SplitVec<_, Doubling> = vec.clone().into();
let mut split_vec_manual = SplitVec::new();
for item in vec {
split_vec_manual.push(item);
}
assert_eq!(split_vec_from, split_vec_manual);
}
}
2 changes: 1 addition & 1 deletion src/growth/recursive/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl<T> From<SplitVec<T, Linear>> for SplitVec<T, Recursive> {
}
}

impl<T: Clone> From<Vec<T>> for SplitVec<T, Recursive> {
impl<T> From<Vec<T>> for SplitVec<T, Recursive> {
/// Converts a `Vec` into a `SplitVec`.
///
/// # Examples
Expand Down