Skip to content
Snippets Groups Projects
buf.rs 5.15 KiB
Newer Older
  • Learn to ignore specific revisions
  • #![feature(test)]
    
    extern crate bytes;
    extern crate test;
    
    use test::Bencher;
    use bytes::Buf;
    use std::io::Cursor;
    
    /// Dummy Buf implementation
    struct TestBuf {
        buf: &'static [u8],
        readlens: &'static [usize],
        init_pos: usize,
        pos: usize,
        readlen_pos: usize,
        readlen: usize,
    }
    impl TestBuf {
        fn new(buf: &'static [u8], readlens: &'static [usize], init_pos: usize) -> TestBuf {
            let mut buf = TestBuf {
                buf,
                readlens,
                init_pos,
                pos: 0,
                readlen_pos: 0,
                readlen: 0,
            };
            buf.reset();
            buf
        }
        fn reset(&mut self) {
            self.pos = self.init_pos;
            self.readlen_pos = 0;
            self.next_readlen();
        }
        /// Compute the length of the next read :
        /// - use the next value specified in readlens (capped by remaining) if any
        /// - else the remaining
        fn next_readlen(&mut self) {
            self.readlen = self.buf.len() - self.pos;
            if let Some(readlen) = self.readlens.get(self.readlen_pos) {
                self.readlen = std::cmp::min(self.readlen, *readlen);
                self.readlen_pos += 1;
            }
        }
    }
    impl Buf for TestBuf {
        fn remaining(&self) -> usize {
            return self.buf.len() - self.pos;
        }
        fn advance(&mut self, cnt: usize) {
            self.pos += cnt;
            assert!(self.pos <= self.buf.len());
            self.next_readlen();
        }
        fn bytes(&self) -> &[u8] {
            if self.readlen == 0 {
                Default::default()
            } else {
                &self.buf[self.pos..self.pos + self.readlen]
            }
        }
    }
    
    /// Dummy Buf implementation
    ///  version with methods forced to not be inlined (to simulate costly calls)
    struct TestBufC {
        inner: TestBuf,
    }
    impl TestBufC {
        fn new(buf: &'static [u8], readlens: &'static [usize], init_pos: usize) -> TestBufC {
            TestBufC {
                inner: TestBuf::new(buf, readlens, init_pos),
            }
        }
        fn reset(&mut self) {
            self.inner.reset()
        }
    }
    impl Buf for TestBufC {
        #[inline(never)]
        fn remaining(&self) -> usize {
            self.inner.remaining()
        }
        #[inline(never)]
        fn advance(&mut self, cnt: usize) {
            self.inner.advance(cnt)
        }
        #[inline(never)]
        fn bytes(&self) -> &[u8] {
            self.inner.bytes()
        }
    }
    
    macro_rules! bench {
        ($fname:ident, testbuf $testbuf:ident $readlens:expr, $method:ident $(,$arg:expr)*) => (
            #[bench]
            fn $fname(b: &mut Bencher) {
                let mut bufs = [
                    $testbuf::new(&[1u8; 8+0], $readlens, 0),
                    $testbuf::new(&[1u8; 8+1], $readlens, 1),
                    $testbuf::new(&[1u8; 8+2], $readlens, 2),
                    $testbuf::new(&[1u8; 8+3], $readlens, 3),
                    $testbuf::new(&[1u8; 8+4], $readlens, 4),
                    $testbuf::new(&[1u8; 8+5], $readlens, 5),
                    $testbuf::new(&[1u8; 8+6], $readlens, 6),
                    $testbuf::new(&[1u8; 8+7], $readlens, 7),
                ];
                b.iter(|| {
                    for i in 0..8 {
                        bufs[i].reset();
                        let mut buf: &mut Buf =  &mut bufs[i]; // type erasure
                        test::black_box(buf.$method($($arg,)*));
                    }
                })
            }
        );
        ($fname:ident, cursor, $method:ident $(,$arg:expr)*) => (
            #[bench]
            fn $fname(b: &mut Bencher) {
                // buf must be long enough for one read of 8 bytes starting at pos 7
                let mut buf = Cursor::new(vec![1u8; 8+7]);
                b.iter(|| {
                    for i in 0..8 {
                        buf.set_position(i);
                        let buf = &mut buf as &mut Buf; // type erasure
                        test::black_box(buf.$method($($arg,)*));
                    }
                })
            }
        );
        ($fname:ident, option) => (
            #[bench]
            fn $fname(b: &mut Bencher) {
                let data = [1u8; 1];
                b.iter(|| {
                    for _ in 0..8 {
                        let mut buf = Some(data);
                        let buf = &mut buf as &mut Buf; // type erasure
                        test::black_box(buf.get_u8());
                    }
                })
            }
        );
    }
    
    macro_rules! bench_group {
        ($method:ident $(,$arg:expr)*) => (
            bench!(cursor, cursor, $method $(,$arg)*);
            bench!(tbuf_1,        testbuf TestBuf  &[],  $method $(,$arg)*);
            bench!(tbuf_1_costly, testbuf TestBufC &[],  $method $(,$arg)*);
            bench!(tbuf_2,        testbuf TestBuf  &[1], $method $(,$arg)*);
            bench!(tbuf_2_costly, testbuf TestBufC &[1], $method $(,$arg)*);
            // bench!(tbuf_onebyone,        testbuf TestBuf  &[1,1,1,1,1,1,1,1], $method $(,$arg)*);
            // bench!(tbuf_onebyone_costly, testbuf TestBufC &[1,1,1,1,1,1,1,1], $method $(,$arg)*);
        );
    }
    
    mod get_u8 {
        use super::*;
        bench_group!(get_u8);
        bench!(option, option);
    }
    mod get_u16 {
        use super::*;
        bench_group!(get_u16);
    }
    mod get_u32 {
        use super::*;
        bench_group!(get_u32);
    }
    mod get_u64 {
        use super::*;
        bench_group!(get_u64);
    }
    mod get_f32 {
        use super::*;
        bench_group!(get_f32);
    }
    mod get_f64 {
        use super::*;
        bench_group!(get_f64);
    }
    mod get_uint24 {
        use super::*;
        bench_group!(get_uint, 3);
    }