TennyZhuang / bench-marked-vec

A simple benchmark
3 stars 0 forks source link

Different performance with `zip` + `filter_map` and `for_each` #1

Open xxchan opened 2 years ago

xxchan commented 2 years ago
test tests::bench1      ... bench:     177,986 ns/iter (+/- 7,900)
test tests::bench2_1    ... bench:     158,979 ns/iter (+/- 5,008)
test tests::bench2_2    ... bench:     159,564 ns/iter (+/- 7,935)
test tests::bench3_1    ... bench:     152,371 ns/iter (+/- 6,054)
test tests::bench3_2    ... bench:     177,288 ns/iter (+/- 7,721)

g is test::black_box

#![feature(test)]
#![feature(core_intrinsics)]
#![feature(generators, generator_trait)]

extern crate test;

pub struct MarkedVec {
    v: Vec<i32>,
    vis: Vec<bool>,
}

impl MarkedVec {
    pub fn iter(&self) -> MarkedVecIter<'_> {
        MarkedVecIter::<'_> {
            v: &self.v,
            vis: &self.vis,
            pos: 0,
        }
    }
}

pub struct MarkedVecIter<'a> {
    v: &'a [i32],
    vis: &'a [bool],
    pos: usize,
}

impl<'a> Iterator for MarkedVecIter<'a> {
    type Item = i32;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        while self.pos < self.v.len() {
            unsafe {
                if *self.vis.get_unchecked(self.pos) {
                    let item = *self.v.get_unchecked(self.pos);
                    self.pos += 1;
                    return Some(item);
                }
                self.pos += 1;
            }
        }
        None
    }
}

#[no_mangle]
pub fn g(v: i32) {
    let _ = test::black_box(v + v + v);
}

pub fn iter1(mv: &MarkedVec) {
    let n = mv.v.len();
    for i in 0..n {
        unsafe {
            if *mv.vis.get_unchecked(i) {
                g(*mv.v.get_unchecked(i));
            }
        }
    }
}

pub fn iter2_1(mv: &MarkedVec) {
    for x in mv.iter() {
        g(x);
    }
}

pub fn iter2_2(mv: &MarkedVec) {
    mv.iter().for_each(g)
}

pub fn iter3_1(mv: &MarkedVec) {
    for x in
        mv.v.iter()
            .zip(&mv.vis)
            .filter_map(|(x, v)| if *v { Some(*x) } else { None })
    {
        g(x)
    }
}

pub fn iter3_2(mv: &MarkedVec) {
    mv.v.iter()
        .zip(&mv.vis)
        .filter_map(|(x, v)| if *v { Some(*x) } else { None })
        .for_each(|x| g(x));
}

pub fn no_op(mv: &MarkedVec) {}

#[inline(never)]
fn init(mv: &mut MarkedVec, n: usize, x: i32, y: i32) {
    mv.v.clear();
    for i in 0..n {
        mv.v.push(x + i as i32);
    }
    mv.vis.clear();
    for i in 0..n {
        mv.vis.push(i % y as usize == 0);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;

    #[test]
    fn it_works() {}

    #[bench]
    fn bench1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter1(&mv);
        });
    }

    #[bench]
    fn bench2_1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter2_1(&mv);
        });
    }

    #[bench]
    fn bench2_2(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter2_2(&mv);
        });
    }

    #[bench]
    fn bench3_1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter3_1(&mv);
        });
    }

    #[bench]
    fn bench3_2(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter3_2(&mv);
        });
    }

    #[bench]
    fn bench_no_op(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            no_op(&mv);
        });
    }
}
xxchan commented 2 years ago

And when I change iter to calculate a sum, the results changed again...

test tests::bench1      ... bench:     161,524 ns/iter (+/- 4,970)
test tests::bench2_1    ... bench:     193,444 ns/iter (+/- 9,360)
test tests::bench2_2    ... bench:     192,883 ns/iter (+/- 7,177)
test tests::bench3_1    ... bench:     162,645 ns/iter (+/- 7,508)
test tests::bench3_2    ... bench:     176,655 ns/iter (+/- 8,012)
#![feature(test)]
#![feature(core_intrinsics)]
#![feature(generators, generator_trait)]

extern crate test;

pub struct MarkedVec {
    v: Vec<i32>,
    vis: Vec<bool>,
}

impl MarkedVec {
    pub fn iter(&self) -> MarkedVecIter<'_> {
        MarkedVecIter::<'_> {
            v: &self.v,
            vis: &self.vis,
            pos: 0,
        }
    }
}

pub struct MarkedVecIter<'a> {
    v: &'a [i32],
    vis: &'a [bool],
    pos: usize,
}

impl<'a> Iterator for MarkedVecIter<'a> {
    type Item = i32;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        while self.pos < self.v.len() {
            unsafe {
                if *self.vis.get_unchecked(self.pos) {
                    let item = *self.v.get_unchecked(self.pos);
                    self.pos += 1;
                    return Some(item);
                }
                self.pos += 1;
            }
        }
        None
    }
}

#[no_mangle]
pub fn g(v: i32) -> i32 {
    v + v + v
}

pub fn iter1(mv: &MarkedVec) -> i32 {
    let mut res = 0;
    let n = mv.v.len();
    for i in 0..n {
        unsafe {
            if *mv.vis.get_unchecked(i) {
                res += g(*mv.v.get_unchecked(i));
            }
        }
    }
    res
}

pub fn iter2_1(mv: &MarkedVec) -> i32 {
    let mut res = 0;
    for x in mv.iter() {
        res += g(x);
    }
    res
}

pub fn iter2_2(mv: &MarkedVec) -> i32 {
    let mut res = 0;
    mv.iter().for_each(|x| res += g(x));
    res
}

pub fn iter3_1(mv: &MarkedVec) -> i32 {
    let mut res = 0;
    for x in
        mv.v.iter()
            .zip(&mv.vis)
            .filter_map(|(x, v)| if *v { Some(*x) } else { None })
    {
        res += g(x);
    }
    res
}

pub fn iter3_2(mv: &MarkedVec) -> i32 {
    let mut res = 0;

    mv.v.iter()
        .zip(&mv.vis)
        .filter_map(|(x, v)| if *v { Some(*x) } else { None })
        .for_each(|x| res += g(x));
    res
}

pub fn no_op(mv: &MarkedVec) {}

#[inline(never)]
fn init(mv: &mut MarkedVec, n: usize, x: i32, y: i32) {
    mv.v.clear();
    for i in 0..n {
        mv.v.push(x + i as i32);
    }
    mv.vis.clear();
    for i in 0..n {
        mv.vis.push(i % y as usize == 0);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;

    #[test]
    fn it_works() {}

    #[bench]
    fn bench1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter1(&mv)
        });
    }

    #[bench]
    fn bench2_1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter2_1(&mv)
        });
    }

    #[bench]
    fn bench2_2(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter2_2(&mv)
        });
    }

    #[bench]
    fn bench3_1(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter3_1(&mv)
        });
    }

    #[bench]
    fn bench3_2(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            iter3_2(&mv)
        });
    }

    #[bench]
    fn bench_no_op(b: &mut Bencher) {
        let n = test::black_box(100000);
        let mut mv = MarkedVec {
            v: Vec::with_capacity(n),
            vis: Vec::with_capacity(n),
        };
        b.iter(|| {
            init(&mut mv, n, 3, 2);
            no_op(&mv);
        });
    }
}