use std::io::Read; use std::time; use lb::buf; use lb::buf::Buffer; use lb::color; use lb::img; use lb::mat; use lb::prelude::*; use lb::typeset; use lb::typesetters; fn main() -> Result<(), Box> { let args: Vec = std::env::args().collect(); if args.len() < 3 { eprintln!("expected at least 2 CLI arguments"); eprintln!("usage: cargo run --example bench -- "); } let size: usize = args[1].as_str().parse()?; let repeats: usize = args[2].as_str().parse()?; let print = args .get(3) .filter(|a| *a == "-p" || *a == "--print") .is_some(); let config = Config { size: (size, size).into(), repeats, print, random: Some(1928346841), }; bench_buffer::(config); bench_buffer::(config); let config = Config { random: None, ..config }; bench_buffer::(config); bench_buffer::(config); let config = Config { random: Some(1928346841), ..config }; bench_typesetter(config, &typesetters::Asymmetric); bench_typesetter(config, &typesetters::Sextant); bench_typesetter(config, &typesetters::Quadrant); bench_typesetter(config, &typesetters::Half); bench_typesetter(config, &typesetters::Block); let config = Config { random: None, ..config }; bench_typesetter(config, &typesetters::Asymmetric); bench_typesetter(config, &typesetters::Sextant); bench_typesetter(config, &typesetters::Quadrant); bench_typesetter(config, &typesetters::Half); bench_typesetter(config, &typesetters::Block); Ok(()) } fn pause() { println!("Press enter to continue..."); std::io::stdin().bytes().next(); } #[derive(Clone, Copy)] struct Config { size: mat::Size, repeats: usize, random: Option, print: bool, } fn bench_buffer(config: Config) where B: Buffer, { if config.print { print!("\x1b[2J"); } let mut buffer = B::new(config.size); let mut fill_times = time::Duration::ZERO; let mut render_times = time::Duration::ZERO; let mut print_times = time::Duration::ZERO; let mut char_count: usize = 0; let mut rnd = config.random.map(|s| rand::Rand::new(s)); for _ in 0..config.repeats { let mut watch = Stopwatch::new(); fill_buffer(&mut buffer, rnd.as_mut()); fill_times += watch.lap(); let string = buffer.render(); char_count += string.len(); render_times += watch.lap(); if config.print { print!("\x1b[1;1H"); print!("{}", string); print_times += watch.lap(); } } println!( "{}, random: {}", std::any::type_name::(), config.random.is_some() ); println!("chars: {}", char_count); println!("fill: {:?}/frame", fill_times / config.repeats as u32); println!("render: {:?}/frame", render_times / config.repeats as u32); if config.print { println!("print: {:?}/frame", print_times / config.repeats as u32); pause(); } println!(); } fn fill_buffer(buffer: &mut B, mut rnd: Option<&mut rand::Rand>) where B: Buffer, { let mut buf = [0u8; 6]; let mut codepoint: char = 'x'; let rows = buffer.rows_mut(); for row in rows { for glyph in row { if let Some(ref mut rnd) = rnd { rnd.fill(&mut buf); // NOTE The random numbers never stop, so unwrap is fine. codepoint = rnd.next().unwrap().clamp(0x20, 0x7e) as char; } let fg = color::Rgb::new(buf[0], buf[1], buf[2]); let bg = color::Rgb::new(buf[3], buf[4], buf[5]); glyph.char = codepoint; glyph.foreground = Some(fg); glyph.background = Some(bg); } } } fn bench_typesetter(config: Config, typesetter: &T) where T: typeset::Typesetter, { if config.print { print!("\x1b[2J"); } let mut buffer = buf::DoubleBuffer::new(config.size); let mut img = img::ImgVec::with_default(buffer.size().to_image_size(typesetter)); let mut rnd = config.random.map(|s| rand::Rand::new(s)); let mut fill_times = time::Duration::ZERO; let mut compose_times = time::Duration::ZERO; let mut render_times = time::Duration::ZERO; let mut print_times = time::Duration::ZERO; for _ in 0..config.repeats { let mut watch = Stopwatch::new(); fill_img(&mut img, rnd.as_mut()); fill_times += watch.lap(); typesetter.compose(buffer.as_mut(), img.as_ref()); compose_times += watch.lap(); if config.print { let string = buffer.render(); render_times += watch.lap(); print!("\x1b[1;1H"); print!("{}", string); print_times += watch.lap(); } } println!( "{}, random: {}", std::any::type_name::(), config.random.is_some() ); println!("fill: {:?}/frame", fill_times / config.repeats as u32); println!("compose: {:?}/frame", compose_times / config.repeats as u32); if config.print { println!("render: {:?}/frame", render_times / config.repeats as u32); println!("print: {:?}/frame", print_times / config.repeats as u32); pause(); } println!(); } fn fill_img(img: &mut img::ImgVec, mut rnd: Option<&mut rand::Rand>) { let mut buf = [0u8; 3]; let rows = img.rows_mut(); for row in rows { for rgb in row { if let Some(ref mut rnd) = rnd { rnd.fill(&mut buf); } *rgb = color::Rgb::new(buf[0], buf[1], buf[2]); } } } struct Stopwatch { last: time::Instant, } impl Stopwatch { pub fn new() -> Self { Self { last: time::Instant::now(), } } pub fn lap(&mut self) -> time::Duration { let last = self.last; self.last = time::Instant::now(); self.last - last } } mod rand { use std::num; pub struct Rand { state: u64, key: u64, buf: [u8; 8], i: usize, } impl Rand { pub fn new(seed: u64) -> Rand { Rand { state: seed, key: DEFAULT_KEY, buf: [0u8; 8], i: 8, } } pub fn fill(&mut self, bytes: &mut [u8]) { for (byte, rnd) in bytes.iter_mut().zip(self) { *byte = rnd; } } } impl Iterator for Rand { type Item = u8; fn next(&mut self) -> Option { if self.i >= self.buf.len() { self.buf = squares64(self.state, self.key).to_ne_bytes(); self.state += 1; self.i = 0; } let i = self.i; self.i += 1; Some(self.buf[i]) } } const DEFAULT_KEY: u64 = 0x1fb9463761e329f5; #[allow(dead_code)] fn squares32(i: u64, key: u64) -> u32 { let i = num::Wrapping(i); let key = num::Wrapping(key); let mut x = i * key; let y = x; let z = y + key; x = x * x + y; x = (x >> 32) | (x << 32); x = x * x + z; x = (x >> 32) | (x << 32); x = x * x + y; x = (x >> 32) | (x << 32); ((x * x + z) >> 32).0 as u32 } fn squares64(i: u64, key: u64) -> u64 { let i = num::Wrapping(i); let key = num::Wrapping(key); let mut x = i * key; let y = x; let z = y + key; x = x * x + y; x = (x >> 32) | (x << 32); x = x * x + z; x = (x >> 32) | (x << 32); x = x * x + y; x = (x >> 32) | (x << 32); x = x * x + z; let t = x; x = (x >> 32) | (x << 32); (t ^ ((x * x + y) >> 32)).0 } #[test] fn test_rand_next() { let seed = 0xf08aca531c67f7a7; let bytes_rand: Vec = Rand::new(seed).take(16).collect(); let fst = squares64(seed, DEFAULT_KEY).to_ne_bytes(); assert_eq!(bytes_rand[..8], fst); let snd = squares64(seed + 1, DEFAULT_KEY).to_ne_bytes(); assert_eq!(bytes_rand[8..], snd); } #[test] fn test_squares32() { let seed = 0x7098117a5705d5b8; assert_eq!(squares32(seed + 0, DEFAULT_KEY), 309968457); assert_eq!(squares32(seed + 1, DEFAULT_KEY), 245018196); } }