From 7c73d32ffa955b966ce4387844a14d5f6075666d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Eng=C3=A9libert?= Date: Fri, 15 Mar 2024 20:15:21 +0100 Subject: [PATCH] v0.3.0 --- Cargo.toml | 18 ++++++++++++------ README.md | 7 +++---- benches/comparison.rs | 42 +++++++++++------------------------------- src/lib.rs | 7 +++++-- src/traits.rs | 2 +- 5 files changed, 32 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 46e9e18..8343c80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,28 +1,34 @@ [package] name = "median-accumulator" -version = "0.2.0" +version = "0.4.0" edition = "2021" authors = ["tuxmain "] license = "AGPL-3.0-only" repository = "https://git.txmn.tk/tuxmain/median-accumulator" documentation = "https://docs.rs/median-accumulator/" -description = "Simple, fast, space-efficient accumulator for computing median" +description = "Simple, fast, space-efficient, generic accumulator for computing median" categories = ["algorithms", "data-structures", "no-std"] +keywords = ["median"] [dependencies] -cc-traits = "1.0.0" +cc-traits = { version = "2.0.0", default_features = false } smallvec = { version = "^1.6", optional = true } [features] -nostd = ["cc-traits/nostd"] +std = ["cc-traits/alloc", "cc-traits/std"] smallvec = ["dep:smallvec", "cc-traits/smallvec"] +default = ["std"] + [dev-dependencies] -criterion = { version = "0.4.0", features = ["html_reports"] } -medianheap = "0.3.0" +criterion = { version = "0.5.1", features = ["html_reports"] } +medianheap = "0.4.1" rand = "0.8.5" smallvec = "^1.6" [[bench]] name = "comparison" harness = false + +[package.metadata.docs.rs] +features = ["std"] diff --git a/README.md b/README.md index 44cea41..5bb954e 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,10 @@ Simple, space-efficient algorithm to compute the median of an accumulation of el * **Space-efficient**: `O(D)` space, D being the number of _different_ samples, not the _total_ number of samples * **Time-efficient**: push is `O(log(N))` * **Generic**: `T: Clone + Ord` -* **Tested** -* **No unsafe**, no deps +* **No unsafe** * **no_std** (optional): supports generic collections -Faster than other implementations if there are samples having the same value. If this is not your case, you should use another implementation. +Faster than other implementations if lots of samples have the same value. If this is not your case, you should use another implementation. ## Use @@ -45,7 +44,7 @@ For other collections than `Vec` or `SmallVec`, you must implement [cc-traits](h ## License -CopyLeft 2022-2023 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/) +CopyLeft 2022-2024 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License. diff --git a/benches/comparison.rs b/benches/comparison.rs index 867ea07..cee27cc 100644 --- a/benches/comparison.rs +++ b/benches/comparison.rs @@ -1,54 +1,34 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use rand::Rng; +static ITERS: u32 = 10_000; + fn compare_crates(c: &mut Criterion) { let mut rng = rand::thread_rng(); let mut group = c.benchmark_group("Comparison"); - for len in [10, 50, 100, 500, 1000] { - let samples: Vec = (0..len).map(|_| rng.gen_range(0..len / 5)).collect(); + for redundancy in [1, 5, 10, 20, 40] { + let samples: Vec = (0..ITERS) + .map(|_| rng.gen_range(0..ITERS / redundancy)) + .collect(); group.bench_with_input( - BenchmarkId::new("median_accumulator 1:5", len), + BenchmarkId::new("median_accumulator", redundancy), &samples, |b, _i| { b.iter(|| { let mut median = median_accumulator::vec::MedianAcc::new(); samples.iter().for_each(|s| median.push(*s)); - median.get_median() + black_box(median.get_median()); }) }, ); group.bench_with_input( - BenchmarkId::new("medianheap 1:5", len), + BenchmarkId::new("medianheap", redundancy), &samples, |b, _i| { b.iter(|| { let mut median = medianheap::MedianHeap::new(); samples.iter().for_each(|s| median.push(*s)); - median.median() - }) - }, - ); - - let samples: Vec = (0..len).map(|_| rng.gen_range(0..len)).collect(); - group.bench_with_input( - BenchmarkId::new("median_accumulator 1:1", len), - &samples, - |b, _i| { - b.iter(|| { - let mut median = median_accumulator::vec::MedianAcc::new(); - samples.iter().for_each(|s| median.push(*s)); - median.get_median() - }) - }, - ); - group.bench_with_input( - BenchmarkId::new("medianheap 1:1", len), - &samples, - |b, _i| { - b.iter(|| { - let mut median = medianheap::MedianHeap::new(); - samples.iter().for_each(|s| median.push(*s)); - median.median() + black_box(median.median()); }) }, ); diff --git a/src/lib.rs b/src/lib.rs index 80dfef7..b742547 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ //! //! In doc comments, _N_ represents the number of samples, _D_ represents the number of different values taken by the samples. -#![cfg_attr(feature = "nostd", no_std)] +#![cfg_attr(not(feature = "std"), no_std)] mod traits; @@ -34,7 +34,7 @@ pub struct MedianAcc< _t: core::marker::PhantomData, } -#[cfg(not(feature = "nostd"))] +#[cfg(feature = "std")] pub mod vec { pub type MedianAcc = crate::MedianAcc>; } @@ -213,6 +213,7 @@ mod tests { use rand::Rng; + #[cfg(feature = "std")] fn naive_median(samples: &mut [T]) -> Option> { if samples.is_empty() { None @@ -232,6 +233,7 @@ mod tests { } } + #[cfg(feature = "std")] #[test] fn correctness() { let mut rng = rand::thread_rng(); @@ -249,6 +251,7 @@ mod tests { } } + #[cfg(feature = "smallvec")] #[test] fn correctness_smallvec() { let mut rng = rand::thread_rng(); diff --git a/src/traits.rs b/src/traits.rs index 222f482..a5132b3 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -9,7 +9,7 @@ pub trait InsertIndex: cc_traits::Collection { ) -> Self::Output; } -#[cfg(not(feature = "nostd"))] +#[cfg(feature = "std")] impl InsertIndex for Vec { type Output = ();