From 701be9315d865ba8a3a9c312a292d7998944760d Mon Sep 17 00:00:00 2001 From: EdJoPaTo Date: Tue, 30 Apr 2024 22:38:16 +0200 Subject: [PATCH] test(bench): add benchmark --- Cargo.toml | 13 +++++ benches/bench.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 benches/bench.rs diff --git a/Cargo.toml b/Cargo.toml index 0072b1b..6d2f274 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,23 @@ unsafe_code = "forbid" pedantic = "warn" nursery = "warn" +[profile.bench] +codegen-units = 1 +debug = true +lto = true + [dependencies] ratatui = { version = "0.26", default-features = false } unicode-width = "0.1" [dev-dependencies] +criterion = "0.5" crossterm = "0.27" ratatui = "0.26" + +[target.'cfg(target_family = "unix")'.dev-dependencies] +pprof = { version = "0.13", features = ["criterion", "flamegraph"] } + +[[bench]] +name = "bench" +harness = false diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 0000000..d2212a7 --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,144 @@ +use std::hint::black_box; + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; +use ratatui::buffer::Buffer; +use ratatui::layout::Rect; +use ratatui::widgets::StatefulWidget; +use tui_tree_widget::{Tree, TreeItem, TreeState}; + +fn example_items() -> Vec> { + vec![ + TreeItem::new_leaf("a", "Alfa"), + TreeItem::new( + "b", + "Bravo", + vec![ + TreeItem::new_leaf("c", "Charlie"), + TreeItem::new( + "d", + "Delta", + vec![ + TreeItem::new_leaf("e", "Echo"), + TreeItem::new_leaf("f", "Foxtrot"), + ], + ) + .expect("all item identifiers are unique"), + TreeItem::new_leaf("g", "Golf"), + ], + ) + .expect("all item identifiers are unique"), + TreeItem::new_leaf("h", "Hotel"), + TreeItem::new( + "i", + "India", + vec![ + TreeItem::new_leaf("j", "Juliett"), + TreeItem::new_leaf("k", "Kilo"), + TreeItem::new_leaf("l", "Lima"), + TreeItem::new_leaf("m", "Mike"), + TreeItem::new_leaf("n", "November"), + ], + ) + .expect("all item identifiers are unique"), + TreeItem::new_leaf("o", "Oscar"), + TreeItem::new( + "p", + "Papa", + vec![ + TreeItem::new_leaf("q", "Quebec"), + TreeItem::new_leaf("r", "Romeo"), + TreeItem::new_leaf("s", "Sierra"), + TreeItem::new_leaf("t", "Tango"), + TreeItem::new_leaf("u", "Uniform"), + TreeItem::new( + "v", + "Victor", + vec![ + TreeItem::new_leaf("w", "Whiskey"), + TreeItem::new_leaf("x", "Xray"), + TreeItem::new_leaf("y", "Yankee"), + ], + ) + .expect("all item identifiers are unique"), + ], + ) + .expect("all item identifiers are unique"), + TreeItem::new_leaf("z", "Zulu"), + ] +} + +fn init(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("init"); + group.throughput(Throughput::Elements(1)); // Frames per second + + group.bench_function("empty", |bencher| { + bencher.iter(|| { + let items: Vec> = vec![]; + black_box(Tree::new(black_box(items))).unwrap(); + }); + }); + + group.bench_function("example-items", |bencher| { + bencher.iter(|| { + let items = example_items(); + black_box(Tree::new(black_box(items))).unwrap(); + }); + }); + + group.finish(); +} + +fn renders(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("render"); + group.throughput(Throughput::Elements(1)); // Frames per second + + let buffer_size = Rect::new(0, 0, 100, 100); + + group.bench_function("empty", |bencher| { + let items: Vec> = vec![]; + let tree = Tree::new(items).unwrap(); + let mut state = TreeState::default(); + bencher.iter_batched( + || (tree.clone(), Buffer::empty(buffer_size)), + |(tree, mut buffer)| { + black_box(tree).render(buffer_size, black_box(&mut buffer), black_box(&mut state)); + }, + BatchSize::SmallInput, + ); + }); + + group.bench_function("example-items", |bencher| { + let items = example_items(); + let tree = Tree::new(items).unwrap(); + let mut state = TreeState::default(); + state.open(vec!["b"]); + state.open(vec!["b", "d"]); + bencher.iter_batched( + || (tree.clone(), Buffer::empty(buffer_size)), + |(tree, mut buffer)| { + black_box(tree).render(buffer_size, black_box(&mut buffer), black_box(&mut state)); + }, + BatchSize::SmallInput, + ); + }); + + group.finish(); +} + +/// Create flamegraphs with `cargo bench --bench bench -- --profile-time=5` +#[cfg(unix)] +fn profiled() -> Criterion { + use pprof::criterion::{Output, PProfProfiler}; + Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))) +} +#[cfg(not(unix))] +fn profiled() -> Criterion { + Criterion::default() +} + +criterion_group! { + name = benches; + config = profiled(); + targets = init, renders +} +criterion_main!(benches);