From ee72b48fea888ca3318ea4ec20a30a976d82b65f Mon Sep 17 00:00:00 2001 From: rhysd Date: Sat, 9 Jul 2022 18:44:00 +0900 Subject: [PATCH] add first benchmark --- CONTRIBUTING.md | 17 ++++++++++ Cargo.toml | 8 +++++ bench/Cargo.toml | 20 ++++++++++++ bench/README.md | 34 ++++++++++++++++++++ bench/benches/insert.rs | 57 +++++++++++++++++++++++++++++++++ bench/src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 207 insertions(+) create mode 100644 bench/Cargo.toml create mode 100644 bench/README.md create mode 100644 bench/benches/insert.rs create mode 100644 bench/src/lib.rs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9067922..1d9926f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,3 +40,20 @@ To run fuzzer: ```sh cargo +nightly fuzz run edit ``` + +## Run benchmark + +Benchmarks are available using [Criterion.rs][criterion]. + +To separate `criterion` crate dependency, benchmark suites are separated as another crate in [bench/](./bench). + +To run benchmarks: + +```sh +cd ./bench +cargo bench --benches +``` + +See [README in bench/](./bench/README.md) for more details. + +[criterion]: https://github.com/bheisler/criterion.rs diff --git a/Cargo.toml b/Cargo.toml index 93baee1..5165f0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,5 +56,13 @@ required-features = ["crossterm"] name = "variable" required-features = ["crossterm"] +[workspace] +members = [ + "bench", +] + +[profile.bench] +lto = "thin" + [package.metadata.docs.rs] all-features = true diff --git a/bench/Cargo.toml b/bench/Cargo.toml new file mode 100644 index 0000000..aaac798 --- /dev/null +++ b/bench/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tui-textarea-bench" +version = "0.0.0" +edition = "2021" +publish = false +license = "MIT" + +[lib] +bench = false + +[dependencies] +tui-textarea = { path = "..", default-features = false } +tui = { version = "0.18", default-features = false } + +[dev-dependencies] +criterion = "0.3" + +[[bench]] +name = "insert" +harness = false diff --git a/bench/README.md b/bench/README.md new file mode 100644 index 0000000..b885a14 --- /dev/null +++ b/bench/README.md @@ -0,0 +1,34 @@ +Benchmarks for tui-textarea using [Criterion.rs][criterion]. + +To run all benchmarks: + +```sh +cargo bench --benches +``` + +To run specific benchmark suite: + +```sh +cargo bench --bench insert +``` + +To filter benchmarks: + +```sh +cargo bench append::1_lorem +``` + +To compare benchmark results with [critcmp][]: + +```sh +git checkout main +cargo bench -- --save-baseline base + +git checkout your-feature +cargo bench -- --save-baseline change + +critcmp base change +``` + +[criterion]: https://github.com/bheisler/criterion.rs +[critcmp]: https://github.com/BurntSushi/critcmp diff --git a/bench/benches/insert.rs b/bench/benches/insert.rs new file mode 100644 index 0000000..cd3627a --- /dev/null +++ b/bench/benches/insert.rs @@ -0,0 +1,57 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use tui_textarea::{Input, Key, TextArea}; +use tui_textarea_bench::dummy_terminal; + +const LOREM: &[&str] = &[ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do", + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim", + "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut", + "aliquip ex ea commodo consequat. Duis aute irure dolor in", + "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla", + "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in", + "culpa qui officia deserunt mollit anim id est laborum.", +]; + +#[inline] +fn append_lorem(repeat: usize) -> usize { + let mut textarea = TextArea::default(); + let mut term = dummy_terminal(); + for _ in 0..repeat { + for line in LOREM { + for c in line.chars() { + textarea.input(Input { + key: Key::Char(c), + ctrl: false, + alt: false, + }); + } + term.draw(|f| { + f.render_widget(textarea.widget(), f.size()); + }) + .unwrap(); + } + textarea.input(Input { + key: Key::Enter, + ctrl: false, + alt: false, + }); + term.draw(|f| { + f.render_widget(textarea.widget(), f.size()); + }) + .unwrap(); + } + textarea.lines().len() +} + +fn append(c: &mut Criterion) { + c.bench_function("append::1_lorem", |b| b.iter(|| black_box(append_lorem(1)))); + c.bench_function("append::10_lorem", |b| { + b.iter(|| black_box(append_lorem(10))) + }); + c.bench_function("append::100_lorem", |b| { + b.iter(|| black_box(append_lorem(100))) + }); +} + +criterion_group!(insert, append); +criterion_main!(insert); diff --git a/bench/src/lib.rs b/bench/src/lib.rs new file mode 100644 index 0000000..3be47a8 --- /dev/null +++ b/bench/src/lib.rs @@ -0,0 +1,71 @@ +// We use empty backend for our benchmark instead of tui::backend::TestBackend to make impact of benchmark from tui-rs +// as small as possible. + +use std::io; +use tui::backend::Backend; +use tui::buffer::Cell; +use tui::layout::Rect; +use tui::Terminal; + +pub struct DummyBackend { + width: u16, + height: u16, + cursor: (u16, u16), +} + +impl Default for DummyBackend { + fn default() -> Self { + Self { + width: 80, + height: 24, + cursor: (0, 0), + } + } +} + +impl Backend for DummyBackend { + fn draw<'a, I>(&mut self, _content: I) -> Result<(), io::Error> + where + I: Iterator, + { + Ok(()) + } + + fn hide_cursor(&mut self) -> Result<(), io::Error> { + Ok(()) + } + + fn show_cursor(&mut self) -> Result<(), io::Error> { + Ok(()) + } + + fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> { + Ok(self.cursor) + } + + fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> { + self.cursor = (x, y); + Ok(()) + } + + fn clear(&mut self) -> Result<(), io::Error> { + Ok(()) + } + + fn size(&self) -> Result { + Ok(Rect { + x: 0, + y: 0, + width: self.width, + height: self.height, + }) + } + + fn flush(&mut self) -> Result<(), io::Error> { + Ok(()) + } +} + +pub fn dummy_terminal() -> Terminal { + Terminal::new(DummyBackend::default()).unwrap() +}