365 lines
11 KiB
Rust
365 lines
11 KiB
Rust
use iris::prelude::*;
|
|
use std::time::Instant;
|
|
|
|
struct BenchRsc {
|
|
ui: UiData,
|
|
}
|
|
|
|
impl UiRsc for BenchRsc {
|
|
fn ui(&self) -> &UiData {
|
|
&self.ui
|
|
}
|
|
fn ui_mut(&mut self) -> &mut UiData {
|
|
&mut self.ui
|
|
}
|
|
}
|
|
|
|
const BODY: &str = "The quick brown fox jumps over the lazy dog. Iris lays \
|
|
out wrapped text by shaping once per width and caching the result, so a \
|
|
row that is offered the same width twice does not reshape. This sentence \
|
|
exists only to give a row enough text to wrap across several lines at a \
|
|
typical phone column width.";
|
|
|
|
fn build_row(rsc: &mut BenchRsc, i: usize, image_every: usize) -> StrongWidget {
|
|
let mut text = Text::new(format!("Message {i}: {BODY}"));
|
|
text.wrap = true;
|
|
let text = rsc.ui.widgets.add_strong(text).any();
|
|
|
|
if image_every > 0 && i.is_multiple_of(image_every) {
|
|
let img = image::DynamicImage::new_rgba8(64, 64);
|
|
let image_widget = image::<BenchRsc>(img)(rsc);
|
|
let image_widget = rsc.ui.widgets.add_strong(image_widget).any();
|
|
let mut row = Span::empty(Dir::DOWN);
|
|
row.push(text);
|
|
row.push(image_widget);
|
|
rsc.ui.widgets.add_strong(row).any()
|
|
} else {
|
|
text
|
|
}
|
|
}
|
|
|
|
fn build_message_list(
|
|
rsc: &mut BenchRsc,
|
|
n: usize,
|
|
image_every: usize,
|
|
) -> (WeakWidget<LazySpan>, StrongWidget) {
|
|
let mut list = LazySpan::new(Dir::DOWN, Pin::End);
|
|
for i in 0..n {
|
|
let row = build_row(rsc, i, image_every);
|
|
list.push_back(LazyItem::new(i as u64, row));
|
|
}
|
|
let list = rsc.ui.widgets.add_strong(list);
|
|
(list.weak(), list.any())
|
|
}
|
|
|
|
fn report(label: &str, elapsed: std::time::Duration, draws: u64, rewrites: u64, moves: u64) {
|
|
println!(
|
|
"{label}: {:.2}ms draws={draws} rewrites={rewrites} moves={moves}",
|
|
elapsed.as_secs_f64() * 1000.0
|
|
);
|
|
}
|
|
|
|
fn bench_first_frame(n: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let (_list, root) = build_message_list(&mut rsc, n, 20);
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
let elapsed = start.elapsed();
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
report(
|
|
&format!("(a) first frame, N={n}"),
|
|
elapsed,
|
|
draws,
|
|
rewrites,
|
|
moves,
|
|
);
|
|
}
|
|
|
|
fn bench_scroll(n: usize, ticks: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let (scroll, root) = build_message_list(&mut rsc, n, 20);
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
render.update(&root, &mut rsc);
|
|
rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(0.0);
|
|
render.update(&root, &mut rsc);
|
|
render.take_counters();
|
|
|
|
let mut total = std::time::Duration::ZERO;
|
|
let mut total_draws = 0u64;
|
|
let mut total_rewrites = 0u64;
|
|
let mut total_moves = 0u64;
|
|
for _ in 0..ticks {
|
|
rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(-8.0);
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
total += start.elapsed();
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
total_draws += draws;
|
|
total_rewrites += rewrites;
|
|
total_moves += moves;
|
|
}
|
|
report(
|
|
&format!("(b) scroll, N={n}, {ticks} ticks (totals; expect draws/moves independent of N)"),
|
|
total,
|
|
total_draws,
|
|
total_rewrites,
|
|
total_moves,
|
|
);
|
|
println!(
|
|
" per-tick average: {:.4}ms",
|
|
total.as_secs_f64() * 1000.0 / ticks as f64
|
|
);
|
|
}
|
|
|
|
fn bench_input_grows(n: usize, lines: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let (scroll, list_root) = build_message_list(&mut rsc, n, 20);
|
|
let list_area = rsc.ui.widgets.add_strong(Sized {
|
|
inner: list_root,
|
|
x: None,
|
|
y: Some(rest(1.0)),
|
|
});
|
|
|
|
let line_height = 24.0;
|
|
let input_rect = rsc.ui.widgets.add_strong(Rect::new(UiColor::WHITE));
|
|
let input_area = rsc.ui.widgets.add_strong(Sized {
|
|
inner: input_rect.any(),
|
|
x: None,
|
|
y: Some(abs(line_height)),
|
|
});
|
|
|
|
let input_area_weak = input_area.weak();
|
|
let mut root_span = Span::empty(Dir::DOWN);
|
|
root_span.push(list_area.any());
|
|
root_span.push(input_area.any());
|
|
let root = rsc.ui.widgets.add_strong(root_span).any();
|
|
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
render.update(&root, &mut rsc);
|
|
rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(0.0);
|
|
render.update(&root, &mut rsc);
|
|
render.take_counters();
|
|
|
|
let mut total = std::time::Duration::ZERO;
|
|
let mut total_draws = 0u64;
|
|
let mut total_rewrites = 0u64;
|
|
let mut total_moves = 0u64;
|
|
for line in 1..=lines {
|
|
rsc.ui.widgets.get_mut(&input_area_weak).unwrap().y =
|
|
Some(abs(line_height * (line + 1) as f32));
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
total += start.elapsed();
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
total_draws += draws;
|
|
total_rewrites += rewrites;
|
|
total_moves += moves;
|
|
}
|
|
report(
|
|
&format!(
|
|
"(c) input grows by {lines} lines above N={n} rows (totals; \
|
|
draws/rewrites must not scale with N)"
|
|
),
|
|
total,
|
|
total_draws,
|
|
total_rewrites,
|
|
total_moves,
|
|
);
|
|
println!(
|
|
" per-line average: {:.4}ms",
|
|
total.as_secs_f64() * 1000.0 / lines as f64
|
|
);
|
|
}
|
|
|
|
fn bench_insert_above_anchor(n: usize, inserts: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let (list, root) = build_message_list(&mut rsc, n, 20);
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
render.update(&root, &mut rsc);
|
|
rsc.ui.widgets.get_mut(&list).unwrap().jump_to_start();
|
|
render.update(&root, &mut rsc);
|
|
render.take_counters();
|
|
|
|
let mut total = std::time::Duration::ZERO;
|
|
let mut total_draws = 0u64;
|
|
let mut total_rewrites = 0u64;
|
|
let mut total_moves = 0u64;
|
|
for i in 0..inserts {
|
|
let row = build_row(&mut rsc, usize::MAX - i, 20);
|
|
rsc.ui
|
|
.widgets
|
|
.get_mut(&list)
|
|
.unwrap()
|
|
.push_front(LazyItem::new(i as u64, row));
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
total += start.elapsed();
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
total_draws += draws;
|
|
total_rewrites += rewrites;
|
|
total_moves += moves;
|
|
}
|
|
report(
|
|
&format!(
|
|
"(d) insert-above-anchor, N={n}, {inserts} pushes (totals; \
|
|
must not scale with N)"
|
|
),
|
|
total,
|
|
total_draws,
|
|
total_rewrites,
|
|
total_moves,
|
|
);
|
|
println!(
|
|
" per-push average: {:.4}ms",
|
|
total.as_secs_f64() * 1000.0 / inserts as f64
|
|
);
|
|
}
|
|
|
|
fn bench_expand_holds_edge(n: usize, growths: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let mut list = LazySpan::new(Dir::DOWN, Pin::End);
|
|
let growable_index = n.saturating_sub(3);
|
|
let mut growable = None;
|
|
for i in 0..n {
|
|
if i == growable_index {
|
|
let rect = rsc.ui.widgets.add_strong(Rect::new(UiColor::WHITE));
|
|
let sized = rsc.ui.widgets.add_strong(Sized {
|
|
inner: rect.any(),
|
|
x: None,
|
|
y: Some(abs(40.0)),
|
|
});
|
|
growable = Some(sized.weak());
|
|
list.push_back(LazyItem::new(i as u64, sized.any()));
|
|
} else {
|
|
let row = build_row(&mut rsc, i, 20);
|
|
list.push_back(LazyItem::new(i as u64, row));
|
|
}
|
|
}
|
|
let list = rsc.ui.widgets.add_strong(list);
|
|
let list_weak = list.weak();
|
|
let root = list.any();
|
|
let growable = growable.unwrap();
|
|
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
render.update(&root, &mut rsc);
|
|
render.take_counters();
|
|
|
|
let mut total = std::time::Duration::ZERO;
|
|
let mut total_draws = 0u64;
|
|
let mut total_rewrites = 0u64;
|
|
let mut total_moves = 0u64;
|
|
let mut height = 40.0f32;
|
|
let key = growable_index as u64;
|
|
for _ in 0..growths {
|
|
height += 10.0;
|
|
if let Some((top, _bottom)) = rsc.ui.widgets.get(&list_weak).unwrap().extent(key) {
|
|
rsc.ui
|
|
.widgets
|
|
.get_mut(&list_weak)
|
|
.unwrap()
|
|
.note_tap(top + 1.0);
|
|
}
|
|
rsc.ui.widgets.get_mut(&growable).unwrap().y = Some(abs(height));
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
total += start.elapsed();
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
total_draws += draws;
|
|
total_rewrites += rewrites;
|
|
total_moves += moves;
|
|
}
|
|
report(
|
|
&format!(
|
|
"(e) expand-hold, N={n}, {growths} growths (totals; \
|
|
must not scale with N)"
|
|
),
|
|
total,
|
|
total_draws,
|
|
total_rewrites,
|
|
total_moves,
|
|
);
|
|
println!(
|
|
" per-growth average: {:.4}ms",
|
|
total.as_secs_f64() * 1000.0 / growths as f64
|
|
);
|
|
}
|
|
|
|
fn bench_redraw_big_text(chars: usize, redraws: usize) {
|
|
let mut rsc = BenchRsc {
|
|
ui: UiData::default(),
|
|
};
|
|
let content: String = (0..chars)
|
|
.map(|i| char::from(b'a' + (i % 26) as u8))
|
|
.collect();
|
|
let mut text = Text::new(content);
|
|
text.wrap = true;
|
|
let text = rsc.ui.widgets.add_strong(text);
|
|
let handle = text.weak();
|
|
let root = text.any();
|
|
|
|
let mut render = UiRenderState::new();
|
|
render.resize((1080.0, 2000.0));
|
|
render.update(&root, &mut rsc);
|
|
render.take_counters();
|
|
|
|
let mut total = std::time::Duration::ZERO;
|
|
for _ in 0..redraws {
|
|
rsc.ui.widgets.get_mut(&handle).unwrap();
|
|
let start = Instant::now();
|
|
render.update(&root, &mut rsc);
|
|
total += start.elapsed();
|
|
}
|
|
let (draws, rewrites, moves, _shapes) = render.take_counters();
|
|
report(
|
|
&format!("(g) redraw one {chars}-glyph text, {redraws}x (totals)"),
|
|
total,
|
|
draws,
|
|
rewrites,
|
|
moves,
|
|
);
|
|
println!(
|
|
" per redraw: {:.3}ms, per glyph: {:.4}us",
|
|
total.as_secs_f64() * 1000.0 / redraws as f64,
|
|
total.as_secs_f64() * 1_000_000.0 / (redraws * chars) as f64,
|
|
);
|
|
}
|
|
|
|
fn main() {
|
|
println!("iris message-list benchmark -- release build, this machine's CPU");
|
|
for &n in &[100usize, 1_000, 10_000] {
|
|
bench_first_frame(n);
|
|
}
|
|
for &n in &[100usize, 1_000, 10_000] {
|
|
bench_scroll(n, 200);
|
|
}
|
|
for &n in &[100usize, 1_000, 10_000] {
|
|
bench_input_grows(n, 40);
|
|
}
|
|
for &n in &[100usize, 1_000, 10_000] {
|
|
bench_insert_above_anchor(n, 200);
|
|
}
|
|
for &n in &[100usize, 1_000, 10_000] {
|
|
bench_expand_holds_edge(n, 40);
|
|
}
|
|
for &chars in &[1_000usize, 10_000, 50_000] {
|
|
bench_redraw_big_text(chars, 10);
|
|
}
|
|
}
|