Skip to content

Commit

Permalink
IArray: from_iter optimization for empty arrays (#48)
Browse files Browse the repository at this point in the history
This will optimize creation of IArray from an empty iterator.

Also added a constant `IArray<T>::EMPTY`.
  • Loading branch information
cecton authored Nov 20, 2023
1 parent 30c7c20 commit c95b92e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 11 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ serde = { version = "1", optional = true }
static_assertions = "1"

[workspace]
members = ["implicit-clone-derive"]
default-members = [".", "implicit-clone-derive"]
members = ["implicit-clone-derive", "benches"]
12 changes: 12 additions & 0 deletions benches/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "benches"
version = "0.1.0"
edition = "2021"

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

[dev-dependencies]
divan = "0.1"
implicit-clone = { path = ".." }
26 changes: 26 additions & 0 deletions benches/benches/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use implicit_clone::unsync::*;

fn main() {
divan::main();
}

#[divan::bench(sample_size = 10000000)]
fn from_iter_empty_collections(bencher: divan::Bencher) {
bencher.bench_local(move || {
let _: IArray<u32> = divan::black_box(Vec::new()).into_iter().collect();
});
}

#[divan::bench(sample_size = 10000000)]
fn from_iter_collection_with_single_element(bencher: divan::Bencher) {
bencher.bench_local(move || {
let _: IArray<u32> = divan::black_box(vec![42]).into_iter().collect();
});
}

#[divan::bench(sample_size = 10000000)]
fn from_iter_collection_with_multiple_elements(bencher: divan::Bencher) {
bencher.bench_local(move || {
let _: IArray<u32> = divan::black_box(vec![42, 43]).into_iter().collect();
});
}
43 changes: 33 additions & 10 deletions src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,23 @@ impl<T: ImplicitClone + 'static> Clone for IArray<T> {

impl<T: ImplicitClone + 'static> Default for IArray<T> {
fn default() -> Self {
Self::Static(&[])
Self::EMPTY
}
}

impl<T: ImplicitClone + 'static> FromIterator<T> for IArray<T> {
fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Self {
let mut it = it.into_iter();
if it.size_hint() == (1, Some(1)) {
Self::Single([it.next().unwrap()])
} else {
Self::Rc(Rc::from(it.collect::<Vec<T>>()))
match it.size_hint() {
(_, Some(0)) => Self::EMPTY,
(_, Some(1)) => {
if let Some(element) = it.next() {
Self::Single([element])
} else {
Self::EMPTY
}
}
_ => Self::Rc(Rc::from_iter(it)),
}
}
}
Expand Down Expand Up @@ -111,6 +117,9 @@ impl<T: ImplicitClone + 'static> Iterator for Iter<T> {
}

impl<T: ImplicitClone + 'static> IArray<T> {
/// An empty array without allocation.
pub const EMPTY: Self = Self::Static(&[]);

/// Returns an iterator over the slice.
///
/// # Examples
Expand Down Expand Up @@ -400,10 +409,24 @@ mod test_array {

#[test]
fn from_iter_is_optimized() {
let array_0 = [].into_iter().collect::<IArray<u32>>();
assert!(matches!(array_0, IArray::Static(_)));
let array_1 = [1].into_iter().collect::<IArray<u32>>();
assert!(matches!(array_1, IArray::Single(_)));
let array_2 = [1, 2].into_iter().collect::<IArray<u32>>();
assert!(matches!(array_2, IArray::Rc(_)));
{
let it = [1].into_iter().filter(|x| x % 2 == 0);
assert_eq!(it.size_hint(), (0, Some(1)));
let array_0_to_1 = it.collect::<IArray<u32>>();
assert!(matches!(array_0_to_1, IArray::Static(_)));
}
{
let it = [2].into_iter().filter(|x| x % 2 == 0);
assert_eq!(it.size_hint(), (0, Some(1)));
let array_0_to_1 = it.collect::<IArray<u32>>();
assert!(matches!(array_0_to_1, IArray::Single(_)));
}
}

#[test]
Expand All @@ -418,19 +441,19 @@ mod test_array {

#[test]
fn tuple_in_array() {
const _ARRAY_2: IArray<(u32, u32)> = IArray::Static(&[]);
const _ARRAY_5: IArray<(u32, u32, u32, u32, u32)> = IArray::Static(&[]);
const _ARRAY_2: IArray<(u32, u32)> = IArray::EMPTY;
const _ARRAY_5: IArray<(u32, u32, u32, u32, u32)> = IArray::EMPTY;
}

#[test]
fn floats_in_array() {
const _ARRAY_F32: IArray<f32> = IArray::Static(&[]);
const _ARRAY_F64: IArray<f64> = IArray::Static(&[]);
const _ARRAY_F32: IArray<f32> = IArray::EMPTY;
const _ARRAY_F64: IArray<f64> = IArray::EMPTY;
}

#[test]
fn from() {
let x: IArray<u32> = IArray::Static(&[]);
let x: IArray<u32> = IArray::EMPTY;
let _out = IArray::from(&x);

let _array: IArray<u32> = IArray::from(&[1, 2, 3][..]);
Expand Down

0 comments on commit c95b92e

Please sign in to comment.