use bitbuffer::{ BigEndian, BitRead, BitReadBuffer, BitReadStream, BitWriteStream, Endianness, LittleEndian, }; use iai_callgrind::{library_benchmark, library_benchmark_group, main}; use std::hint::black_box; const ONES: &[u8; 1024 * 1024 * 10] = &[1u8; 1024 * 1024 * 10]; const ONES_LE: BitReadBuffer<'static, LittleEndian> = BitReadBuffer::new(ONES, LittleEndian); const ONES_BE: BitReadBuffer<'static, BigEndian> = BitReadBuffer::new(ONES, BigEndian); #[library_benchmark] #[bench::le(ONES_LE)] #[bench::be(ONES_BE)] fn read_perf(buffer: BitReadBuffer) -> u16 { let size = 5; let mut pos = 0; let len = buffer.bit_len(); let mut result: u16 = 0; loop { if pos + size > len { return black_box(result); } let data = buffer.read_int::(pos, size).unwrap() as u16; result = result.wrapping_add(data); pos += size; } } #[library_benchmark] #[bench::le(ONES_LE)] #[bench::be(ONES_BE)] fn perf_f32(buffer: BitReadBuffer) -> f32 { let mut pos = 0; let len = buffer.bit_len(); let mut result: f32 = 0.0; loop { if pos + 32 > len { break; } let num = buffer.read_float::(pos).unwrap(); result += num; pos += 32; } assert_eq!(result, 0.00000000000000000000000000000006170106); black_box(result) } const F64_RESULT: f64 = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010156250477904244; #[library_benchmark] #[bench::le(ONES_LE)] #[bench::be(ONES_BE)] fn perf_f64(buffer: BitReadBuffer) -> f64 { let mut pos = 0; let len = buffer.bit_len(); let mut result: f64 = 0.0; loop { if pos + 64 > len { break; } let num = buffer.read_float::(pos).unwrap(); result += num; pos += 64; } assert_eq!(result, F64_RESULT); black_box(result) } #[library_benchmark] #[bench::le(ONES_LE)] #[bench::be(ONES_BE)] fn perf_bool(buffer: BitReadBuffer) { let mut pos = 0; let len = buffer.bit_len() / 8; loop { if pos >= len { break; } let num = buffer.read_bool(pos).unwrap(); black_box(num); pos += 1; } } fn build_string_buffer( offset: usize, endianness: E, ) -> (usize, BitReadBuffer<'static, E>) { let mut data = Vec::new(); let input = [ "foo\0", "bar\0", "something a little bit longer for extra testing\0", "a\0", "\0", ] .join(""); let mut writer = BitWriteStream::new(&mut data, endianness); writer.write_int(0usize, offset).unwrap(); while writer.byte_len() < 10 * 1024 { writer.write(&input).unwrap() } (offset, BitReadBuffer::new_owned(data, endianness)) } #[library_benchmark(setup = build_string_buffer)] #[bench::le_alligned(0, LittleEndian)] #[bench::be_alligned(0, BigEndian)] #[bench::le_unalligned(3, LittleEndian)] #[bench::be_unalligned(3, BigEndian)] fn perf_string((offset, buffer): (usize, BitReadBuffer)) { let mut pos = offset; let len = buffer.bit_len(); loop { if pos + (128 * 8) > len { break; } let result = buffer.read_string(pos, None).unwrap(); pos += (result.len() + 1) * 8; black_box(result); } } #[library_benchmark(setup = build_string_buffer)] #[bench::le_alligned(0, LittleEndian)] #[bench::be_alligned(0, BigEndian)] #[bench::le_unalligned(3, LittleEndian)] #[bench::be_unalligned(3, BigEndian)] fn perf_string_into((offset, buffer): (usize, BitReadBuffer)) { let mut pos = offset; let len = buffer.bit_len(); let mut buff = String::new(); loop { if pos + (128 * 8) > len { break; } let result = buffer.read_string_into(pos, None, &mut buff).unwrap(); let result = result.as_ref(&buff); pos += (result.len() + 1) * 8; black_box(result); } } #[library_benchmark(setup = build_string_buffer)] #[bench::le_alligned(0, LittleEndian)] #[bench::be_alligned(0, BigEndian)] #[bench::le_unalligned(3, LittleEndian)] #[bench::be_unalligned(3, BigEndian)] fn perf_bytes((offset, buffer): (usize, BitReadBuffer)) { let mut pos = offset; let len = buffer.bit_len(); loop { if pos + (128 * 8) > len { break; } let result = buffer.read_bytes(pos, 128).unwrap(); pos += (result.len() + 1) * 8; black_box(result); } } #[library_benchmark(setup = build_string_buffer)] #[bench::le_alligned(0, LittleEndian)] #[bench::be_alligned(0, BigEndian)] #[bench::le_unalligned(3, LittleEndian)] #[bench::be_unalligned(3, BigEndian)] fn perf_bytes_into((offset, buffer): (usize, BitReadBuffer)) { let mut pos = offset; let len = buffer.bit_len(); let mut buff = Vec::new(); loop { if pos + (128 * 8) > len { break; } let result = buffer.read_bytes_into(pos, 128, &mut buff).unwrap(); let result = result.as_ref(&buff); pos += (result.len() + 1) * 8; black_box(result); } } #[allow(dead_code)] #[derive(BitRead)] struct BasicStruct { a: f32, b: bool, #[size = 7] c: u32, } const BASIC: BasicStruct = BasicStruct { a: 0.0, b: false, c: 0, }; #[allow(dead_code)] #[derive(BitRead)] #[discriminant_bits = 2] enum BasicEnum { #[size = 5] Foo(i8), Bar(bool), Asd(u8), Empty, } const ENUM: BasicEnum = BasicEnum::Empty; #[library_benchmark] #[bench::le(ONES_LE, BASIC)] #[bench::be(ONES_BE, BASIC)] #[bench::le_enum(ONES_LE, ENUM)] #[bench::be_enum(ONES_BE, ENUM)] fn perf_struct>( buffer: BitReadBuffer, _struct: Struct, ) { let mut stream: BitReadStream = buffer.into(); while stream.bits_left() > 40 { let result = stream.read::().unwrap(); black_box(result); } } library_benchmark_group!( name = bench_read_primitives; benchmarks = read_perf, perf_bool, perf_f32, perf_f64 ); library_benchmark_group!( name = bench_read_string; compare_by_id = true; benchmarks = perf_string, perf_string_into ); library_benchmark_group!( name = bench_read_bytes; compare_by_id = true; benchmarks = perf_bytes, perf_bytes_into ); library_benchmark_group!( name = bench_read_struct; benchmarks = perf_struct ); main!( library_benchmark_groups = bench_read_primitives, bench_read_string, bench_read_bytes, bench_read_struct );