use core::{hint::black_box, num::NonZeroUsize};
use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use downsample::{FixedFrequencyBuilder, Reducer, reducers};
fn nz(value: usize) -> NonZeroUsize { NonZeroUsize::new(value).unwrap() }
fn day_history_average() -> downsample::FixedWriter<f32> {
FixedFrequencyBuilder::new(nz(100))
.level(nz(59), nz(100), reducers::average_f32())
.level(nz(59), nz(60), reducers::average_f32())
.level(nz(23), nz(60), reducers::average_f32())
.build()
}
fn dense_rollover_average() -> downsample::FixedWriter<f32> {
FixedFrequencyBuilder::new(nz(16))
.level(nz(16), nz(4), reducers::average_f32())
.level(nz(16), nz(4), reducers::average_f32())
.level(nz(16), nz(4), reducers::average_f32())
.level(nz(16), nz(4), reducers::average_f32())
.build()
}
fn median_rollover() -> downsample::FixedWriter<f32> {
FixedFrequencyBuilder::new(nz(64))
.level(nz(32), nz(64), reducers::median_f32())
.level(nz(32), nz(16), reducers::median_f32())
.build()
}
fn fill(writer: &mut downsample::FixedWriter<f32>, count: usize) {
for value in 0..count {
writer.push((value % 10_000) as f32 * 0.25);
}
}
fn builder(c: &mut Criterion) {
c.bench_function("builder/day_history_average", |b| {
b.iter(|| black_box(day_history_average()))
});
}
fn push_single(c: &mut Criterion) {
let mut writer = day_history_average();
c.bench_function("push/single_steady_state_average", |b| {
b.iter(|| writer.push(black_box(123.456)));
});
}
fn push_batches(c: &mut Criterion) {
let mut group = c.benchmark_group("push/batch");
for count in [1_000, 100_000] {
group.throughput(Throughput::Elements(count as u64));
group.bench_with_input(BenchmarkId::new("day_history_average", count), &count, |b, &count| {
b.iter_batched(
day_history_average,
|mut writer| {
for value in 0..count {
writer.push(black_box(value as f32));
}
black_box(writer)
},
BatchSize::SmallInput,
);
});
}
group.throughput(Throughput::Elements(100_000));
group.bench_function("dense_rollover_average/100000", |b| {
b.iter_batched(
dense_rollover_average,
|mut writer| {
for value in 0..100_000 {
writer.push(black_box(value as f32));
}
black_box(writer)
},
BatchSize::SmallInput,
);
});
group.throughput(Throughput::Elements(10_000));
group.bench_function("median_rollover/10000", |b| {
b.iter_batched(
median_rollover,
|mut writer| {
for value in 0..10_000 {
writer.push(black_box((value % 251) as f32));
}
black_box(writer)
},
BatchSize::SmallInput,
);
});
group.finish();
}
fn read(c: &mut Criterion) {
let mut writer = day_history_average();
fill(&mut writer, 100_000);
let mut group = c.benchmark_group("read");
group.throughput(Throughput::Elements(1_000_000));
group.bench_function("indexed_level_lookup", |b| {
b.iter(|| {
for index in 0..1_000_000 {
black_box(writer.storage().get_back_by_level(1, index % 59));
}
});
});
let view = writer.storage().level(1).unwrap();
group.throughput(Throughput::Elements(view.len() as u64));
group.bench_function("iterate_level_oldest_first", |b| {
b.iter(|| {
let mut total = 0.0;
for value in view.iter_oldest_first() {
total += *black_box(value);
}
black_box(total)
});
});
group.finish();
}
fn reduce_many<T>(reducer: Reducer<T>, first: &[T], second: &[T], iterations: usize) -> T
where
T: Copy + Default,
{
let mut value = T::default();
for _ in 0..iterations {
value = reducer.reduce(black_box(first), black_box(second));
black_box(value);
}
value
}
fn reducers(c: &mut Criterion) {
let contiguous_f32: Vec<f32> = (0..1024).map(|value| value as f32 * 0.25).collect();
let split_f32_a: Vec<f32> = (0..257).map(|value| value as f32).collect();
let split_f32_b: Vec<f32> = (257..1024).map(|value| value as f32).collect();
let median_u32: Vec<u32> = (0..1024).rev().collect();
let mut group = c.benchmark_group("reducers");
group.throughput(Throughput::Elements(1024));
group.bench_function("average_f32/contiguous_1024", |b| {
b.iter(|| {
black_box(reducers::average_f32().reduce(black_box(&contiguous_f32), black_box(&[])));
});
});
group.bench_function("average_f32/split_1024", |b| {
b.iter(|| {
black_box(reducers::average_f32().reduce(black_box(&split_f32_a), black_box(&split_f32_b)));
});
});
group.bench_function("min_f32/split_1024", |b| {
b.iter(|| {
black_box(reducers::min::<f32>().reduce(black_box(&split_f32_a), black_box(&split_f32_b)));
});
});
group.bench_function("median_u32/contiguous_1024", |b| {
b.iter(|| {
black_box(reducers::median::<u32>().reduce(black_box(&median_u32), black_box(&[])));
});
});
group.bench_function("median_f32/split_1024", |b| {
b.iter(|| {
black_box(reducers::median_f32().reduce(black_box(&split_f32_a), black_box(&split_f32_b)));
});
});
group.throughput(Throughput::Elements(1024 * 1_000));
group.bench_function("average_f32/split_1024_x1000", |b| {
b.iter(|| black_box(reduce_many(reducers::average_f32(), &split_f32_a, &split_f32_b, 1_000)));
});
group.finish();
}
criterion_group!(benches, builder, push_single, push_batches, read, reducers);
criterion_main!(benches);