criterion_stats/univariate/
mod.rs

1//! Univariate analysis
2
3mod bootstrap;
4mod percentiles;
5mod resamples;
6mod sample;
7
8pub mod kde;
9pub mod mixed;
10pub mod outliers;
11
12use float::Float;
13use num_cpus;
14use std::cmp;
15use thread_scoped as thread;
16
17use tuple::{Tuple, TupledDistributionsBuilder};
18
19use self::resamples::Resamples;
20
21pub use self::percentiles::Percentiles;
22pub use self::sample::Sample;
23
24/// Performs a two-sample bootstrap
25///
26/// - Multithreaded
27/// - Time: `O(nresamples)`
28/// - Memory: `O(nresamples)`
29#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
30pub fn bootstrap<A, B, T, S>(
31    a: &Sample<A>,
32    b: &Sample<B>,
33    nresamples: usize,
34    statistic: S,
35) -> T::Distributions
36where
37    A: Float,
38    B: Float,
39    S: Fn(&Sample<A>, &Sample<B>) -> T,
40    S: Sync,
41    T: Tuple,
42    T::Distributions: Send,
43    T::Builder: Send,
44{
45    let ncpus = num_cpus::get();
46
47    unsafe {
48        // TODO need some sensible threshold to trigger the multi-threaded path
49        if true {
50            //ncpus > 1 && nresamples > a.len() + b.len() {
51            let granularity = nresamples / ncpus + 1;
52            let granularity_sqrt = (granularity as f64).sqrt().ceil() as usize;
53            let statistic = &statistic;
54            let mut cutoff = 0;
55
56            let chunks = (0..ncpus)
57                .map(|_| {
58                    let mut sub_distributions: T::Builder =
59                        TupledDistributionsBuilder::new(granularity);
60                    let start = cutoff;
61                    let end = cmp::min(start + granularity, nresamples);
62                    cutoff = end;
63
64                    thread::scoped(move || {
65                        let mut a_resamples = Resamples::new(a);
66                        let mut b_resamples = Resamples::new(b);
67                        let mut i = start;
68
69                        for _ in 0..granularity_sqrt {
70                            let a_resample = a_resamples.next();
71
72                            for _ in 0..granularity_sqrt {
73                                if i == end {
74                                    return sub_distributions;
75                                }
76
77                                let b_resample = b_resamples.next();
78
79                                sub_distributions.push(statistic(a_resample, b_resample));
80
81                                i += 1;
82                            }
83                        }
84                        sub_distributions
85                    })
86                })
87                .collect::<Vec<_>>();
88
89            let mut builder: T::Builder = TupledDistributionsBuilder::new(nresamples);
90            for chunk in chunks {
91                builder.extend(&mut (chunk.join()));
92            }
93            builder.complete()
94        } else {
95            let nresamples_sqrt = (nresamples as f64).sqrt().ceil() as usize;
96            let mut a_resamples = Resamples::new(a);
97            let mut b_resamples = Resamples::new(b);
98            let mut distributions: T::Builder = TupledDistributionsBuilder::new(nresamples);
99
100            let mut i = 0;
101            'outer: for _ in 0..nresamples_sqrt {
102                let a_resample = a_resamples.next();
103
104                for _ in 0..nresamples_sqrt {
105                    if i == nresamples {
106                        break 'outer;
107                    }
108
109                    let b_resample = b_resamples.next();
110
111                    distributions.push(statistic(a_resample, b_resample));
112
113                    i += 1;
114                }
115            }
116
117            distributions.complete()
118        }
119    }
120}