use core::{hint::black_box, iter::FusedIterator};
use criterion::{criterion_group, criterion_main, Criterion};
use first_err::FirstErr;
mod l1res {
use super::*;
struct L1Iter {
curr: u64,
err_at: Option<u64>,
}
impl L1Iter {
fn new(err_at: Option<u64>) -> Self {
Self { curr: 0, err_at }
}
}
impl Iterator for L1Iter {
type Item = Result<u64, u64>;
fn next(&mut self) -> Option<Self::Item> {
let tmp = self.curr;
self.curr += 1;
let res = if Some(tmp) != self.err_at {
Some(Ok(tmp))
} else {
Some(Err(tmp))
};
black_box(res)
}
}
impl FusedIterator for L1Iter {}
#[inline(never)]
fn first_err_approach(iter: impl Iterator<Item = Result<u64, u64>>) -> Result<u64, u64> {
iter.first_err_or_else(|iter1| iter1.sum::<u64>())
}
#[inline(never)]
fn loop_approach(iter: impl Iterator<Item = Result<u64, u64>>) -> Result<u64, u64> {
let mut sum = 0;
for res in iter {
sum += res?;
}
Ok::<u64, u64>(sum)
}
#[inline(never)]
fn collect_approach(iter: impl Iterator<Item = Result<u64, u64>>) -> Result<u64, u64> {
let sum = iter
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum::<u64>();
Ok(sum)
}
pub fn bench_setup(c: &mut Criterion, err_at: Option<u64>) {
let length: usize = 100_000;
let group_name = match err_at {
Some(err_at) => format!("l1res::err_at_{err_at:_<7}"),
None => format!("l1res::err_not_exists"),
};
{
let collect_ans = black_box(collect_approach(black_box(
L1Iter::new(err_at).take(length),
)));
assert_eq!(
collect_ans,
black_box(loop_approach(black_box(L1Iter::new(err_at).take(length)))),
"loop approach test in: {group_name}",
);
assert_eq!(
collect_ans,
black_box(first_err_approach(black_box(
L1Iter::new(err_at).take(length)
))),
"first_err approach test in: {group_name}",
);
}
{
let mut group = c.benchmark_group(group_name);
group.bench_function("__collect", |b| {
b.iter(|| {
black_box(collect_approach(black_box(
L1Iter::new(err_at).take(length),
)))
})
});
group.bench_function("_____loop", |b| {
b.iter(|| black_box(loop_approach(black_box(L1Iter::new(err_at).take(length)))))
});
group.bench_function("first_err", |b| {
b.iter(|| {
black_box(first_err_approach(black_box(
L1Iter::new(err_at).take(length),
)))
})
});
group.finish();
}
}
}
mod l2res {
use super::*;
struct L2Iter {
curr: u64,
l1_err_at: Option<u64>,
l2_err_at: Option<u64>,
}
impl L2Iter {
fn new(l1_err_at: Option<u64>, l2_err_at: Option<u64>) -> Self {
Self {
curr: 0,
l1_err_at,
l2_err_at,
}
}
}
impl Iterator for L2Iter {
type Item = Result<Result<u64, u64>, u64>;
fn next(&mut self) -> Option<Self::Item> {
let tmp = self.curr;
self.curr += 1;
let l2_res = if Some(tmp) != self.l2_err_at {
Ok(tmp)
} else {
Err(tmp)
};
let l1_res = if Some(tmp) != self.l1_err_at {
Some(Ok(l2_res))
} else {
Some(Err(tmp))
};
black_box(l1_res)
}
}
impl FusedIterator for L2Iter {}
#[inline(never)]
fn first_err_approach(
iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
iter.first_err_or_else(|iter1| iter1.first_err_or_else(|iter2| iter2.sum::<u64>()))
.and_then(|res| res)
}
#[inline(never)]
fn loop_approach(
mut iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
let mut sum = 0;
let mut inner_first_err: Option<u64> = None;
while let Some(outer_res) = iter.next() {
let inner_res = outer_res?;
match inner_res {
Ok(v) => {
sum += v;
}
Err(e) => {
inner_first_err = Some(e);
for outer_res in iter {
let _ = outer_res?;
}
break;
}
}
}
if let Some(e) = inner_first_err {
return Err(e);
}
Ok::<u64, u64>(sum)
}
#[inline(never)]
fn collect_approach(
iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
let sum = iter
.collect::<Result<Vec<Result<u64, u64>>, u64>>()?
.into_iter()
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum::<u64>();
Ok::<u64, u64>(sum)
}
pub fn bench_setup(c: &mut Criterion, l1_err_at: Option<u64>, l2_err_at: Option<u64>) {
let length: usize = 100_000;
let group_name = match (l1_err_at, l2_err_at) {
(Some(l1_err_at), Some(l2_err_at)) => {
format!("l2res::l1_err_at_{l1_err_at:_<7}_l2_err_at_{l2_err_at:_<7}")
}
(Some(l1_err_at), None) => {
format!("l2res::l1_err_at_{l1_err_at:_<7}_l2_err_not_exists")
}
(None, Some(l2_err_at)) => {
format!("l2res::l1_err_not_exists_l2_err_at_{l2_err_at:_<7}")
}
(None, None) => format!("l2res::l1_err_not_exists_l2_err_not_exists"),
};
{
let collect_ans = black_box(collect_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length),
)));
assert_eq!(
collect_ans,
black_box(loop_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length)
))),
"loop approach test in: {group_name}",
);
assert_eq!(
collect_ans,
black_box(first_err_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length)
))),
"first_err approach test in: {group_name}",
);
}
{
let mut group = c.benchmark_group(group_name);
group.bench_function("__collect", |b| {
b.iter(|| {
black_box(collect_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length),
)))
})
});
group.bench_function("_____loop", |b| {
b.iter(|| {
black_box(loop_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length),
)))
})
});
group.bench_function("first_err", |b| {
b.iter(|| {
black_box(first_err_approach(black_box(
L2Iter::new(l1_err_at, l2_err_at).take(length),
)))
})
});
group.finish();
}
}
}
mod l1opt {
use super::*;
struct L1Iter {
curr: u64,
none_at: Option<u64>,
}
impl L1Iter {
fn new(none_at: Option<u64>) -> Self {
Self { curr: 0, none_at }
}
}
impl Iterator for L1Iter {
type Item = Option<u64>;
fn next(&mut self) -> Option<Self::Item> {
let tmp = self.curr;
self.curr += 1;
let res = if Some(tmp) != self.none_at {
Some(Some(tmp))
} else {
Some(None)
};
black_box(res)
}
}
impl FusedIterator for L1Iter {}
#[inline(never)]
fn first_err_approach(iter: impl Iterator<Item = Option<u64>>) -> Option<u64> {
iter.first_none_or_else(|iter1| iter1.sum::<u64>())
}
#[inline(never)]
fn loop_approach(iter: impl Iterator<Item = Option<u64>>) -> Option<u64> {
let mut sum = 0;
for opt in iter {
sum += opt?;
}
Some(sum)
}
#[inline(never)]
fn collect_approach(iter: impl Iterator<Item = Option<u64>>) -> Option<u64> {
let sum = iter.collect::<Option<Vec<u64>>>()?.into_iter().sum::<u64>();
Some(sum)
}
pub fn bench_setup(c: &mut Criterion, none_at: Option<u64>) {
let length: usize = 100_000;
let group_name = match none_at {
Some(none_at) => format!("l1opt::none_at_{none_at:_<7}"),
None => format!("l1opt::none_not_exists"),
};
{
let collect_ans = black_box(collect_approach(black_box(
L1Iter::new(none_at).take(length),
)));
assert_eq!(
collect_ans,
black_box(loop_approach(black_box(L1Iter::new(none_at).take(length)))),
"loop approach test in: {group_name}",
);
assert_eq!(
collect_ans,
black_box(first_err_approach(black_box(
L1Iter::new(none_at).take(length)
))),
"first_err approach test in: {group_name}",
);
}
{
let mut group = c.benchmark_group(group_name);
group.bench_function("__collect", |b| {
b.iter(|| {
black_box(collect_approach(black_box(
L1Iter::new(none_at).take(length),
)))
})
});
group.bench_function("_____loop", |b| {
b.iter(|| black_box(loop_approach(black_box(L1Iter::new(none_at).take(length)))))
});
group.bench_function("first_err", |b| {
b.iter(|| {
black_box(first_err_approach(black_box(
L1Iter::new(none_at).take(length),
)))
})
});
group.finish();
}
}
}
mod l2opt {
use super::*;
struct L2Iter {
curr: u64,
l1_none_at: Option<u64>,
l2_none_at: Option<u64>,
}
impl L2Iter {
fn new(l1_none_at: Option<u64>, l2_none_at: Option<u64>) -> Self {
Self {
curr: 0,
l1_none_at,
l2_none_at,
}
}
}
impl Iterator for L2Iter {
type Item = Result<Result<u64, u64>, u64>;
fn next(&mut self) -> Option<Self::Item> {
let tmp = self.curr;
self.curr += 1;
let l2_res = if Some(tmp) != self.l2_none_at {
Ok(tmp)
} else {
Err(tmp)
};
let l1_res = if Some(tmp) != self.l1_none_at {
Some(Ok(l2_res))
} else {
Some(Err(tmp))
};
black_box(l1_res)
}
}
impl FusedIterator for L2Iter {}
#[inline(never)]
fn first_err_approach(
iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
iter.first_err_or_else(|iter1| iter1.first_err_or_else(|iter2| iter2.sum::<u64>()))
.and_then(|res| res)
}
#[inline(never)]
fn loop_approach(
mut iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
let mut sum = 0;
let mut inner_first_err: Option<u64> = None;
while let Some(outer_res) = iter.next() {
let inner_res = outer_res?;
match inner_res {
Ok(v) => {
sum += v;
}
Err(e) => {
inner_first_err = Some(e);
for outer_res in iter {
let _ = outer_res?;
}
break;
}
}
}
if let Some(e) = inner_first_err {
return Err(e);
}
Ok::<u64, u64>(sum)
}
#[inline(never)]
fn collect_approach(
iter: impl Iterator<Item = Result<Result<u64, u64>, u64>>,
) -> Result<u64, u64> {
let sum = iter
.collect::<Result<Vec<Result<u64, u64>>, u64>>()?
.into_iter()
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum::<u64>();
Ok::<u64, u64>(sum)
}
pub fn bench_setup(c: &mut Criterion, l1_none_at: Option<u64>, l2_none_at: Option<u64>) {
let length: usize = 100_000;
let group_name = match (l1_none_at, l2_none_at) {
(Some(l1_none_at), Some(l2_none_at)) => {
format!("l2opt::l1_none_at_{l1_none_at:_<7}_l2_none_at_{l2_none_at:_<7}")
}
(Some(l1_none_at), None) => {
format!("l2opt::l1_none_at_{l1_none_at:_<7}_l2_none_not_exists")
}
(None, Some(l2_none_at)) => {
format!("l2opt::l1_none_not_exists_l2_none_at_{l2_none_at:_<7}")
}
(None, None) => format!("l2opt::l1_none_not_exists_l2_none_not_exists"),
};
{
let collect_ans = black_box(collect_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length),
)));
assert_eq!(
collect_ans,
black_box(loop_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length)
))),
"loop approach test in: {group_name}",
);
assert_eq!(
collect_ans,
black_box(first_err_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length)
))),
"first_err approach test in: {group_name}",
);
}
{
let mut group = c.benchmark_group(group_name);
group.bench_function("__collect", |b| {
b.iter(|| {
black_box(collect_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length),
)))
})
});
group.bench_function("_____loop", |b| {
b.iter(|| {
black_box(loop_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length),
)))
})
});
group.bench_function("first_err", |b| {
b.iter(|| {
black_box(first_err_approach(black_box(
L2Iter::new(l1_none_at, l2_none_at).take(length),
)))
})
});
group.finish();
}
}
}
fn benchmarks(c: &mut Criterion) {
l1res::bench_setup(c, Some(0));
l1res::bench_setup(c, Some(10));
l1res::bench_setup(c, Some(100));
l1res::bench_setup(c, Some(1000));
l1res::bench_setup(c, Some(10000));
l1res::bench_setup(c, Some(99999));
l1res::bench_setup(c, None);
l2res::bench_setup(c, Some(0), Some(1000));
l2res::bench_setup(c, Some(10), Some(1000));
l2res::bench_setup(c, Some(100), Some(1000));
l2res::bench_setup(c, Some(1000), Some(1000));
l2res::bench_setup(c, Some(10000), Some(1000));
l2res::bench_setup(c, Some(99999), Some(1000));
l2res::bench_setup(c, None, Some(1000));
l2res::bench_setup(c, Some(1000), Some(0));
l2res::bench_setup(c, Some(1000), Some(10));
l2res::bench_setup(c, Some(1000), Some(100));
l2res::bench_setup(c, Some(1000), Some(1000));
l2res::bench_setup(c, Some(1000), Some(10000));
l2res::bench_setup(c, Some(1000), Some(99999));
l2res::bench_setup(c, Some(1000), None);
l2res::bench_setup(c, None, Some(0));
l2res::bench_setup(c, None, Some(10));
l2res::bench_setup(c, None, Some(100));
l2res::bench_setup(c, None, Some(1000));
l2res::bench_setup(c, None, Some(10000));
l2res::bench_setup(c, None, Some(99999));
l2res::bench_setup(c, Some(0), None);
l2res::bench_setup(c, Some(10), None);
l2res::bench_setup(c, Some(100), None);
l2res::bench_setup(c, Some(1000), None);
l2res::bench_setup(c, Some(10000), None);
l2res::bench_setup(c, Some(99999), None);
l2res::bench_setup(c, None, None);
l1opt::bench_setup(c, Some(0));
l1opt::bench_setup(c, Some(10));
l1opt::bench_setup(c, Some(100));
l1opt::bench_setup(c, Some(1000));
l1opt::bench_setup(c, Some(10000));
l1opt::bench_setup(c, Some(99999));
l1opt::bench_setup(c, None);
l2opt::bench_setup(c, Some(0), Some(1000));
l2opt::bench_setup(c, Some(10), Some(1000));
l2opt::bench_setup(c, Some(100), Some(1000));
l2opt::bench_setup(c, Some(1000), Some(1000));
l2opt::bench_setup(c, Some(10000), Some(1000));
l2opt::bench_setup(c, Some(99999), Some(1000));
l2opt::bench_setup(c, None, Some(1000));
l2opt::bench_setup(c, Some(1000), Some(0));
l2opt::bench_setup(c, Some(1000), Some(10));
l2opt::bench_setup(c, Some(1000), Some(100));
l2opt::bench_setup(c, Some(1000), Some(1000));
l2opt::bench_setup(c, Some(1000), Some(10000));
l2opt::bench_setup(c, Some(1000), Some(99999));
l2opt::bench_setup(c, Some(1000), None);
l2opt::bench_setup(c, None, Some(0));
l2opt::bench_setup(c, None, Some(10));
l2opt::bench_setup(c, None, Some(100));
l2opt::bench_setup(c, None, Some(1000));
l2opt::bench_setup(c, None, Some(10000));
l2opt::bench_setup(c, None, Some(99999));
l2opt::bench_setup(c, Some(0), None);
l2opt::bench_setup(c, Some(10), None);
l2opt::bench_setup(c, Some(100), None);
l2opt::bench_setup(c, Some(1000), None);
l2opt::bench_setup(c, Some(10000), None);
l2opt::bench_setup(c, Some(99999), None);
l2opt::bench_setup(c, None, None);
}
criterion_group!(benches, benchmarks);
criterion_main!(benches);