use std::str::from_utf8; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion}; use lazy_format::make_lazy_format; use once_cell::sync::Lazy; use quanta::Instant; use quicklog::serialize::{Serialize, Store}; use quicklog::with_flush; use quicklog_clock::quanta::QuantaClock; use quicklog_clock::Clock; use quicklog_flush::noop_flusher::NoopFlusher; #[derive(Debug, Clone, Copy)] struct BigStruct { vec: [i32; 100], some: &'static str, } #[derive(Debug, Clone)] struct Nested { pub vec: Vec, } impl Serialize for BigStruct { fn encode(&self, write_buf: &'static mut [u8]) -> Store { fn decode(buf: &[u8]) -> String { let (mut _head, mut tail) = buf.split_at(0); let mut vec = vec![]; for _ in 0..100 { (_head, tail) = tail.split_at(4); vec.push(i32::from_le_bytes(_head.try_into().unwrap())); } let s = from_utf8(tail).unwrap(); format!("vec: {:?}, str: {}", vec, s) } let (mut _head, mut tail) = write_buf.split_at_mut(0); for i in 0..100 { (_head, tail) = tail.split_at_mut(4); _head.copy_from_slice(&self.vec[i].to_le_bytes()) } tail.copy_from_slice(self.some.as_bytes()); Store::new(decode, write_buf) } fn buffer_size_required(&self) -> usize { std::mem::size_of::() * 100 + self.some.len() } } macro_rules! loop_with_cleanup { ($bencher:expr, $loop_f:expr) => { loop_with_cleanup!($bencher, $loop_f, { quicklog::flush!() }) }; ($bencher:expr, $loop_f:expr, $cleanup_f:expr) => {{ quicklog::init!(); $bencher.iter_custom(|iters| { let start = Instant::now(); for _i in 0..iters { $loop_f; } let end = Instant::now() - start; $cleanup_f; end }) }}; } fn bench_lazy_format(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "the quick brown fox jumps over the lazy dog", }); let mut nested = black_box(Nested { vec: Vec::new() }); for _ in 0..10 { nested.vec.push(bs) } b.iter(|| { let arg = nested.to_owned(); black_box(make_lazy_format!(|f| { write!( f, concat!("[{}]\t", "{:?}"), quicklog::level::Level::Info, arg ) })); }) } fn bench_box_lazy_format(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "the quick brown fox jumps over the lazy dog", }); let mut nested = black_box(Nested { vec: Vec::new() }); for _ in 0..10 { nested.vec.push(bs) } b.iter(|| { let arg = nested.to_owned(); black_box(Box::new(make_lazy_format!(|f| { write!( f, concat!("[{}]\t", "{:?}"), quicklog::level::Level::Info, arg ) }))); }) } static CLOCK: Lazy = Lazy::new(QuantaClock::new); fn bench_clock(b: &mut Bencher) { b.iter(|| black_box(CLOCK.get_instant())) } type Object = Box; static mut CHANNEL: Lazy<(Sender, Receiver)> = Lazy::new(channel); fn bench_channel_send(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "The quick brown fox jumps over the lazy dog", }); let mut nested = black_box(Nested { vec: Vec::new() }); let mut senders = Vec::new(); for _ in 0..10 { nested.vec.push(bs); unsafe { senders.push(CHANNEL.0.clone()); } } loop_with_cleanup!( b, { let arg = nested.clone(); unsafe { CHANNEL.0.send(Box::new(arg)).unwrap_or(()); } }, { while unsafe { CHANNEL.1.recv_timeout(Duration::from_millis(10)).is_ok() } {} } ) } fn bench_to_owned_nested_struct(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "The quick brown fox jumps over the lazy dog", }); let mut nested = black_box(Nested { vec: Vec::new() }); for _ in 0..10 { nested.vec.push(bs) } b.iter(|| { black_box(nested.to_owned()); }) } fn bench_format_nested_struct(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "the quick brown fox jumps over the lazy dog", }); let mut nested = black_box(Nested { vec: Vec::new() }); for _ in 0..10 { nested.vec.push(bs) } b.iter(|| { black_box(format!("{:?}", nested)); }) } fn bench_logger_no_args(b: &mut Bencher) { with_flush!(NoopFlusher); loop_with_cleanup!( b, quicklog::info!("The quick brown fox jumps over the lazy dog.") ); } fn bench_logger_serialize(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "The quick brown fox jumps over the lazy dog", }); with_flush!(NoopFlusher); loop_with_cleanup!(b, quicklog::info!("Here's some text {}", ^bs)); } fn bench_logger_and_flush(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "the quick brown fox jumps over the lazy dog", }); loop_with_cleanup!(b, quicklog::info!("Here's some text {:?}", bs)); } fn bench_logger_pass_by_ref(b: &mut Bencher) { let bs = black_box(BigStruct { vec: [1; 100], some: "The quick brown fox jumps over the lazy dog", }); with_flush!(NoopFlusher); loop_with_cleanup!(b, quicklog::info!("Here's some text {:?}", &bs)); } fn bench_loggers(c: &mut Criterion) { let mut group = c.benchmark_group("Loggers"); group.bench_function("bench clock", bench_clock); group.bench_function("bench lazy_format", bench_lazy_format); group.bench_function("bench to_owned Nested", bench_to_owned_nested_struct); group.bench_function("bench Channel send", bench_channel_send); group.bench_function("bench box Nested lazy_format", bench_box_lazy_format); group.bench_function("bench format Nested", bench_format_nested_struct); group.bench_function("bench log BigStruct serialize", bench_logger_serialize); group.bench_function("bench log BigStruct", bench_logger_and_flush); group.bench_function("bench log BigStruct ref", bench_logger_pass_by_ref); group.bench_function("bench log no args", bench_logger_no_args); group.finish(); } criterion_group!(benches, bench_loggers); criterion_main!(benches);