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<dyn std::error::Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 3 {
eprintln!("expected at least 2 CLI arguments");
eprintln!("usage: cargo run --example bench -- <buffer-size> <repeats>");
}
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::<buf::DoubleBuffer>(config);
bench_buffer::<buf::SimpleBuffer>(config);
let config = Config {
random: None,
..config
};
bench_buffer::<buf::DoubleBuffer>(config);
bench_buffer::<buf::SimpleBuffer>(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<u64>,
print: bool,
}
fn bench_buffer<B>(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::<B>(),
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<B>(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);
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<T>(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::<T>(),
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<u8> {
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<u8> = 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);
}
}