pub(crate) fn map<T, R, F>(items: &[T], f: F) -> Vec<R>
where
T: Sync,
R: Send,
F: Fn(&T) -> R + Sync + Send,
{
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
{
use rayon::prelude::*;
items.par_iter().map(f).collect()
}
#[cfg(not(all(feature = "parallel", not(target_arch = "wasm32"))))]
{
items.iter().map(f).collect()
}
}
pub(crate) fn join<A, B, RA, RB>(a: A, b: B) -> (RA, RB)
where
A: FnOnce() -> RA + Send,
B: FnOnce() -> RB + Send,
RA: Send,
RB: Send,
{
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
{
rayon::join(a, b)
}
#[cfg(not(all(feature = "parallel", not(target_arch = "wasm32"))))]
{
(a(), b())
}
}
pub(crate) fn fill_chunks<F>(a: &mut [f64], b: &mut [f64], chunk: usize, f: F)
where
F: Fn(usize, &mut [f64], &mut [f64]) + Sync + Send,
{
debug_assert!(b.is_empty() || b.len() == a.len());
let chunk = chunk.max(1);
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
{
use rayon::prelude::*;
if b.is_empty() {
a.par_chunks_mut(chunk)
.enumerate()
.for_each(|(k, a)| f(k * chunk, a, &mut []));
} else {
a.par_chunks_mut(chunk)
.zip(b.par_chunks_mut(chunk))
.enumerate()
.for_each(|(k, (a, b))| f(k * chunk, a, b));
}
}
#[cfg(not(all(feature = "parallel", not(target_arch = "wasm32"))))]
{
if b.is_empty() {
for (k, a) in a.chunks_mut(chunk).enumerate() {
f(k * chunk, a, &mut []);
}
} else {
for (k, (a, b)) in a.chunks_mut(chunk).zip(b.chunks_mut(chunk)).enumerate() {
f(k * chunk, a, b);
}
}
}
}
pub(crate) fn fill_chunks3<F>(a: &mut [f64], b: &mut [f64], c: &mut [f64], chunk: usize, f: F)
where
F: Fn(usize, &mut [f64], &mut [f64], &mut [f64]) + Sync + Send,
{
debug_assert!(b.len() == a.len() && c.len() == a.len());
let chunk = chunk.max(1);
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
{
use rayon::prelude::*;
a.par_chunks_mut(chunk)
.zip(b.par_chunks_mut(chunk))
.zip(c.par_chunks_mut(chunk))
.enumerate()
.for_each(|(k, ((a, b), c))| f(k * chunk, a, b, c));
}
#[cfg(not(all(feature = "parallel", not(target_arch = "wasm32"))))]
{
for (k, ((a, b), c)) in a
.chunks_mut(chunk)
.zip(b.chunks_mut(chunk))
.zip(c.chunks_mut(chunk))
.enumerate()
{
f(k * chunk, a, b, c);
}
}
}
#[cfg(all(test, feature = "parallel", not(target_arch = "wasm32")))]
mod tests {
use crate::{Agg, Column, Reducer, Sequence, TimeSeries};
#[test]
fn same_bits_on_one_and_many_threads() {
let mut x = 0x2545_f491_4f6c_dd1du64;
let mut next = || {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x
};
let n = 20_000;
let mut t = 0i64;
let times: Vec<i64> = (0..n)
.map(|_| {
t += (next() % 3) as i64 * 500;
t
})
.collect();
let cols: Vec<Column> = (0..6)
.map(|c| {
let v: Vec<Option<f64>> = (0..n)
.map(|_| {
let r = next();
(r % 17 != 0).then(|| (r % 1_000_003) as f64 / 7.0 - 70_000.0)
})
.collect();
Column::optional(format!("c{c}"), v)
})
.collect();
let s = TimeSeries::new("s", times, cols).unwrap();
let reducers = [
Reducer::Sum,
Reducer::Avg,
Reducer::Stdev,
Reducer::Median,
Reducer::Min,
Reducer::Percentile(90.0),
];
let aggs: Vec<Agg> = (0..6)
.flat_map(|c| {
reducers
.iter()
.enumerate()
.map(move |(i, r)| Agg::new(format!("c{c}_{i}"), format!("c{c}"), *r))
})
.collect();
let run = |threads: usize| {
rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build()
.unwrap()
.install(|| {
let roll = s.rolling("30s", &aggs).unwrap();
let agg = s.aggregate(&Sequence::every("1m").unwrap(), &aggs).unwrap();
[roll, agg]
.iter()
.flat_map(|r| {
r.columns()
.iter()
.map(|c| {
(
c.name().to_string(),
c.numbers()
.unwrap()
.values()
.iter()
.map(|v| v.to_bits())
.collect(),
)
})
.collect::<Vec<(String, Vec<u64>)>>()
})
.collect::<Vec<_>>()
})
};
let one = run(1);
assert_eq!(one.len(), 72);
assert_eq!(one, run(4));
assert_eq!(one, run(7));
}
}