use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput};
use rotulus_layout::markdown::parse_inline;
use rotulus_layout::{ChatBuffer, FixedMeasure, LayoutParams, Message, Speaker};
use std::hint::black_box;
const WIDTH: u32 = 800;
const VIEWPORT: u32 = 600;
const SIZES: [usize; 3] = [2_000, 20_000, 200_000];
const WORDS: &[&str] = &[
"hotline",
"server",
"anyone",
"around",
"tonight",
"the",
"files",
"are",
"up",
"again",
"**bold**",
"`code`",
"https://example.org/x",
"ok",
"lol",
"mac",
"classic",
"news",
"brb",
];
const NICKS: &[&str] = &["al", "misha", "hx_fan_1999", "zed", "somebody_longer"];
struct Lcg(u64);
impl Lcg {
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0 >> 33
}
}
fn corpus(n: usize) -> Vec<Message> {
let mut rng = Lcg(0x6874_6b68);
(0..n)
.map(|i| {
let words = 3 + (rng.next() % 17) as usize;
let text: Vec<&str> = (0..words)
.map(|_| WORDS[rng.next() as usize % WORDS.len()])
.collect();
let nick = NICKS[i % NICKS.len()];
Message::live(
Speaker::new((i % 64) as u64, nick),
parse_inline(&text.join(" ")),
)
.with_timestamp(1_700_000_000 + i as i64)
})
.collect()
}
fn params() -> LayoutParams {
LayoutParams {
width: WIDTH,
..LayoutParams::default()
}
}
fn filled(n: usize, measure: &FixedMeasure) -> ChatBuffer {
let mut b = ChatBuffer::new(params());
for m in corpus(n) {
b.append(m, measure);
}
b
}
fn paint(b: &mut ChatBuffer, measure: &FixedMeasure) -> usize {
let y = b.scroll_offset(VIEWPORT);
b.ensure_visible(y, VIEWPORT, measure).len()
}
fn bench_ingest(c: &mut Criterion) {
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("ingest");
g.sample_size(10);
for n in SIZES {
g.throughput(Throughput::Elements(n as u64));
g.bench_with_input(BenchmarkId::from_parameter(n), &n, |bch, &n| {
bch.iter_batched(
|| corpus(n),
|msgs| {
let mut b = ChatBuffer::new(params());
for m in msgs {
b.append(m, &measure);
}
b
},
BatchSize::LargeInput,
);
});
}
g.finish();
}
fn bench_first_paint(c: &mut Criterion) {
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("first_paint");
g.sample_size(10);
for n in SIZES {
g.bench_with_input(BenchmarkId::from_parameter(n), &n, |bch, &n| {
bch.iter_batched(
|| filled(n, &measure),
|mut b| {
let rows = paint(&mut b, &measure);
(b, rows)
},
BatchSize::LargeInput,
);
});
}
g.finish();
}
fn bench_relayout(c: &mut Criterion) {
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("relayout");
for n in SIZES {
let mut b = filled(n, &measure);
paint(&mut b, &measure);
let mut font = 0;
g.bench_function(BenchmarkId::from_parameter(n), |bch| {
bch.iter(|| {
font += 1;
b.set_font_generation(font);
black_box(paint(&mut b, &measure))
});
});
}
g.finish();
}
fn bench_scroll_walk(c: &mut Criterion) {
const FRAMES: u64 = 120;
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("scroll_walk");
g.sample_size(10);
g.throughput(Throughput::Elements(FRAMES));
for n in SIZES {
g.bench_with_input(BenchmarkId::from_parameter(n), &n, |bch, &n| {
bch.iter_batched(
|| filled(n, &measure),
|mut b| {
let mut rows = 0;
for f in 0..FRAMES {
let y = f * u64::from(VIEWPORT / 3);
b.scroll_to(y, VIEWPORT, 0);
rows += paint(&mut b, &measure);
}
(b, rows)
},
BatchSize::LargeInput,
);
});
}
g.finish();
}
fn bench_live_at_cap(c: &mut Criterion) {
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("live_at_cap");
for n in SIZES {
let mut b = filled(n, &measure);
b.set_max_rows(n, &measure);
paint(&mut b, &measure);
let mut more = corpus(1024).into_iter().cycle();
g.bench_function(BenchmarkId::from_parameter(n), |bch| {
bch.iter_batched(
|| more.next().expect("cycle"),
|m| {
b.append(m, &measure);
paint(&mut b, &measure)
},
BatchSize::SmallInput,
);
});
}
g.finish();
}
fn bench_search(c: &mut Criterion) {
let measure = FixedMeasure::new(8);
let mut g = c.benchmark_group("search");
for n in SIZES {
let b = filled(n, &measure);
g.throughput(Throughput::Elements(n as u64));
g.bench_function(BenchmarkId::from_parameter(n), |bch| {
bch.iter(|| black_box(b.search(black_box("Classic"), false).len()));
});
}
g.finish();
}
fn bench_markdown(c: &mut Criterion) {
let line = "the **files** are up again, see `news` or https://example.org/x ~~lol~~ *brb*";
c.bench_function("parse_inline", |bch| {
bch.iter(|| black_box(parse_inline(black_box(line))));
});
}
criterion_group!(
benches,
bench_markdown,
bench_ingest,
bench_first_paint,
bench_relayout,
bench_scroll_walk,
bench_live_at_cap,
bench_search
);
criterion_main!(benches);