зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-09-07 16:05:50 +03:00
chore(bench): restructure into focused targets, add runner, and tune slow cases (#10)
- Upgrade Criterion 0.5 → 0.8.2 - Split 4 broad bench files into 14 focused targets - Add Rust runner binary with per-target start/done logging - Add wrap_render benchmarks (all 4 WrapMode variants × 2 sizes) - Add undo_redo benchmarks (undo/redo × 3 history depths) - Apply benchmark_group + SamplingMode::Flat + reduced sample_size on slow insert cases to cut total run time from ~28 min to ~12 min - Rewrite bench/README.md to document runner, targets, and workflow
Этот коммит содержится в:
@@ -1,171 +0,0 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::{CursorMove, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Restore {
|
||||
TopLeft,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Restore {
|
||||
fn cursor_move(self) -> Option<CursorMove> {
|
||||
match self {
|
||||
Self::TopLeft => Some(CursorMove::Jump(0, 0)),
|
||||
Self::BottomLeft => Some(CursorMove::Jump(u16::MAX, 0)),
|
||||
Self::BottomRight => Some(CursorMove::Jump(u16::MAX, u16::MAX)),
|
||||
Self::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_textarea() -> TextArea<'static> {
|
||||
let mut lines = Vec::with_capacity(LOREM.len() * 2 + 1);
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
lines.push("".to_string());
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
TextArea::new(lines)
|
||||
}
|
||||
|
||||
fn run(
|
||||
mut textarea: TextArea<'_>,
|
||||
moves: &[CursorMove],
|
||||
restore: Restore,
|
||||
repeat: usize,
|
||||
) -> (usize, usize) {
|
||||
let mut term = dummy_terminal();
|
||||
|
||||
let mut prev = textarea.cursor();
|
||||
for _ in 0..repeat {
|
||||
for m in moves {
|
||||
textarea.move_cursor(*m);
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
if let Some(m) = restore.cursor_move() {
|
||||
if textarea.cursor() == prev {
|
||||
textarea.move_cursor(m);
|
||||
prev = textarea.cursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.cursor()
|
||||
}
|
||||
|
||||
fn move_char(c: &mut Criterion) {
|
||||
let textarea = prepare_textarea();
|
||||
c.bench_function("cursor::char::forward", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Forward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::back", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Back],
|
||||
Restore::BottomRight,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::down", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Down],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::up", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Up],
|
||||
Restore::BottomLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
fn move_word(c: &mut Criterion) {
|
||||
let textarea = prepare_textarea();
|
||||
c.bench_function("cursor::word::forward", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::WordForward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::word::back", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::WordBack],
|
||||
Restore::BottomRight,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
fn move_paragraph(c: &mut Criterion) {
|
||||
let textarea = prepare_textarea();
|
||||
c.bench_function("cursor::paragraph::down", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::ParagraphForward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::paragraph::up", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::ParagraphBack],
|
||||
Restore::BottomLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
fn move_edge(c: &mut Criterion) {
|
||||
let textarea = prepare_textarea();
|
||||
c.bench_function("cursor::edge::head_end", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::End, CursorMove::Head],
|
||||
Restore::None,
|
||||
500,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::edge::top_bottom", |b| {
|
||||
b.iter(|| {
|
||||
black_box(run(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Bottom, CursorMove::Top],
|
||||
Restore::None,
|
||||
500,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(cursor, move_char, move_word, move_paragraph, move_edge);
|
||||
criterion_main!(cursor);
|
||||
50
bench/benches/cursor_char.rs
Обычный файл
50
bench/benches/cursor_char.rs
Обычный файл
@@ -0,0 +1,50 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::CursorMove;
|
||||
use tui_textarea_bench::{prepare_cursor_textarea, run_cursor, Restore};
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let textarea = prepare_cursor_textarea();
|
||||
c.bench_function("cursor::char::forward", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Forward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::back", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Back],
|
||||
Restore::BottomRight,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::down", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Down],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::char::up", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Up],
|
||||
Restore::BottomLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(cursor_char, bench);
|
||||
criterion_main!(cursor_char);
|
||||
30
bench/benches/cursor_edge.rs
Обычный файл
30
bench/benches/cursor_edge.rs
Обычный файл
@@ -0,0 +1,30 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::CursorMove;
|
||||
use tui_textarea_bench::{prepare_cursor_textarea, run_cursor, Restore};
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let textarea = prepare_cursor_textarea();
|
||||
c.bench_function("cursor::edge::head_end", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::End, CursorMove::Head],
|
||||
Restore::None,
|
||||
500,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::edge::top_bottom", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::Bottom, CursorMove::Top],
|
||||
Restore::None,
|
||||
500,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(cursor_edge, bench);
|
||||
criterion_main!(cursor_edge);
|
||||
30
bench/benches/cursor_paragraph.rs
Обычный файл
30
bench/benches/cursor_paragraph.rs
Обычный файл
@@ -0,0 +1,30 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::CursorMove;
|
||||
use tui_textarea_bench::{prepare_cursor_textarea, run_cursor, Restore};
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let textarea = prepare_cursor_textarea();
|
||||
c.bench_function("cursor::paragraph::down", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::ParagraphForward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::paragraph::up", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::ParagraphBack],
|
||||
Restore::BottomLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(cursor_paragraph, bench);
|
||||
criterion_main!(cursor_paragraph);
|
||||
30
bench/benches/cursor_word.rs
Обычный файл
30
bench/benches/cursor_word.rs
Обычный файл
@@ -0,0 +1,30 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::CursorMove;
|
||||
use tui_textarea_bench::{prepare_cursor_textarea, run_cursor, Restore};
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let textarea = prepare_cursor_textarea();
|
||||
c.bench_function("cursor::word::forward", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::WordForward],
|
||||
Restore::TopLeft,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
c.bench_function("cursor::word::back", |b| {
|
||||
b.iter(|| {
|
||||
std::hint::black_box(run_cursor(
|
||||
textarea.clone(),
|
||||
&[CursorMove::WordBack],
|
||||
Restore::BottomRight,
|
||||
1000,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(cursor_word, bench);
|
||||
criterion_main!(cursor_word);
|
||||
@@ -2,25 +2,13 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::{CursorMove, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Kind {
|
||||
Char,
|
||||
Word,
|
||||
Line,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn run(textarea: &TextArea<'_>, kind: Kind) {
|
||||
fn run(textarea: &TextArea<'_>) {
|
||||
let mut term = dummy_terminal();
|
||||
let mut t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
for _ in 0..100 {
|
||||
let modified = match kind {
|
||||
Kind::Char => t.delete_char(),
|
||||
Kind::Word => t.delete_word(),
|
||||
Kind::Line => t.delete_line_by_head(),
|
||||
};
|
||||
if !modified {
|
||||
if !t.delete_char() {
|
||||
t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
}
|
||||
@@ -34,11 +22,8 @@ fn bench(c: &mut Criterion) {
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
}
|
||||
let textarea = TextArea::new(lines);
|
||||
|
||||
c.bench_function("delete::char", |b| b.iter(|| run(&textarea, Kind::Char)));
|
||||
c.bench_function("delete::word", |b| b.iter(|| run(&textarea, Kind::Word)));
|
||||
c.bench_function("delete::line", |b| b.iter(|| run(&textarea, Kind::Line)));
|
||||
c.bench_function("delete::char", |b| b.iter(|| run(&textarea)));
|
||||
}
|
||||
|
||||
criterion_group!(delete, bench);
|
||||
criterion_main!(delete);
|
||||
criterion_group!(delete_char, bench);
|
||||
criterion_main!(delete_char);
|
||||
29
bench/benches/delete_line.rs
Обычный файл
29
bench/benches/delete_line.rs
Обычный файл
@@ -0,0 +1,29 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::{CursorMove, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[inline]
|
||||
fn run(textarea: &TextArea<'_>) {
|
||||
let mut term = dummy_terminal();
|
||||
let mut t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
for _ in 0..100 {
|
||||
if !t.delete_line_by_head() {
|
||||
t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
}
|
||||
term.draw_textarea(&t);
|
||||
}
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut lines = vec![];
|
||||
for _ in 0..10 {
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
}
|
||||
let textarea = TextArea::new(lines);
|
||||
c.bench_function("delete::line", |b| b.iter(|| run(&textarea)));
|
||||
}
|
||||
|
||||
criterion_group!(delete_line, bench);
|
||||
criterion_main!(delete_line);
|
||||
29
bench/benches/delete_word.rs
Обычный файл
29
bench/benches/delete_word.rs
Обычный файл
@@ -0,0 +1,29 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::{CursorMove, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[inline]
|
||||
fn run(textarea: &TextArea<'_>) {
|
||||
let mut term = dummy_terminal();
|
||||
let mut t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
for _ in 0..100 {
|
||||
if !t.delete_word() {
|
||||
t = textarea.clone();
|
||||
t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
|
||||
}
|
||||
term.draw_textarea(&t);
|
||||
}
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut lines = vec![];
|
||||
for _ in 0..10 {
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
}
|
||||
let textarea = TextArea::new(lines);
|
||||
c.bench_function("delete::word", |b| b.iter(|| run(&textarea)));
|
||||
}
|
||||
|
||||
criterion_group!(delete_word, bench);
|
||||
criterion_main!(delete_word);
|
||||
@@ -1,129 +0,0 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use tui_textarea::{CursorMove, Input, Key, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM, SEED};
|
||||
|
||||
#[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,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
textarea.input(Input {
|
||||
key: Key::Enter,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn random_lorem(repeat: usize) -> usize {
|
||||
let mut rng = SmallRng::from_seed(SEED);
|
||||
let mut textarea = TextArea::default();
|
||||
let mut term = dummy_terminal();
|
||||
|
||||
for _ in 0..repeat {
|
||||
for line in LOREM {
|
||||
let row = rng.gen_range(0..textarea.lines().len() as u16);
|
||||
textarea.move_cursor(CursorMove::Jump(row, 0));
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
textarea.input(Input {
|
||||
key: Key::Enter,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
|
||||
for c in line.chars() {
|
||||
textarea.input(Input {
|
||||
key: Key::Char(c),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_long_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,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
fn append(c: &mut Criterion) {
|
||||
c.bench_function("insert::append::1_lorem", |b| {
|
||||
b.iter(|| black_box(append_lorem(1)))
|
||||
});
|
||||
c.bench_function("insert::append::10_lorem", |b| {
|
||||
b.iter(|| black_box(append_lorem(10)))
|
||||
});
|
||||
c.bench_function("insert::append::50_lorem", |b| {
|
||||
b.iter(|| black_box(append_lorem(50)))
|
||||
});
|
||||
}
|
||||
|
||||
fn random(c: &mut Criterion) {
|
||||
c.bench_function("insert::random::1_lorem", |b| {
|
||||
b.iter(|| black_box(random_lorem(1)))
|
||||
});
|
||||
c.bench_function("insert::random::10_lorem", |b| {
|
||||
b.iter(|| black_box(random_lorem(10)))
|
||||
});
|
||||
c.bench_function("insert::random::50_lorem", |b| {
|
||||
b.iter(|| black_box(random_lorem(50)))
|
||||
});
|
||||
}
|
||||
|
||||
// Inserting a long line is slower than multiple short lines into `TextArea`
|
||||
fn long(c: &mut Criterion) {
|
||||
c.bench_function("insert::long::1_lorem", |b| {
|
||||
b.iter(|| black_box(append_long_lorem(1)))
|
||||
});
|
||||
c.bench_function("insert::long::5_lorem", |b| {
|
||||
b.iter(|| black_box(append_long_lorem(5)))
|
||||
});
|
||||
c.bench_function("insert::long::10_lorem", |b| {
|
||||
b.iter(|| black_box(append_long_lorem(10)))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(insert, append, random, long);
|
||||
criterion_main!(insert);
|
||||
57
bench/benches/insert_append.rs
Обычный файл
57
bench/benches/insert_append.rs
Обычный файл
@@ -0,0 +1,57 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
|
||||
use tui_textarea::{Input, Key, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[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,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
textarea.input(Input {
|
||||
key: Key::Enter,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("insert::append");
|
||||
|
||||
// ~16ms/iter — fits 100 samples in ~6s, keep defaults
|
||||
group.bench_function("1_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_lorem(1)))
|
||||
});
|
||||
|
||||
// ~490ms/iter — 20 samples × 490ms ≈ 10s
|
||||
group.sampling_mode(SamplingMode::Flat);
|
||||
group.sample_size(20);
|
||||
group.bench_function("10_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_lorem(10)))
|
||||
});
|
||||
|
||||
// ~5.9s/iter — 10 samples × 5.9s ≈ 59s (minimum viable)
|
||||
group.sample_size(10);
|
||||
group.bench_function("50_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_lorem(50)))
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(insert_append, bench);
|
||||
criterion_main!(insert_append);
|
||||
53
bench/benches/insert_long.rs
Обычный файл
53
bench/benches/insert_long.rs
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Inserting a long line is slower than multiple short lines into TextArea
|
||||
use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
|
||||
use tui_textarea::{Input, Key, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[inline]
|
||||
fn append_long_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,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("insert::long");
|
||||
|
||||
// ~25ms/iter — fits 100 samples in ~6s, keep defaults
|
||||
group.bench_function("1_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_long_lorem(1)))
|
||||
});
|
||||
|
||||
// ~324ms/iter — 20 samples × 324ms ≈ 6s
|
||||
group.sampling_mode(SamplingMode::Flat);
|
||||
group.sample_size(20);
|
||||
group.bench_function("5_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_long_lorem(5)))
|
||||
});
|
||||
|
||||
// ~1.1s/iter — 10 samples × 1.1s ≈ 11s
|
||||
group.sample_size(10);
|
||||
group.bench_function("10_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(append_long_lorem(10)))
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(insert_long, bench);
|
||||
criterion_main!(insert_long);
|
||||
67
bench/benches/insert_random.rs
Обычный файл
67
bench/benches/insert_random.rs
Обычный файл
@@ -0,0 +1,67 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use tui_textarea::{CursorMove, Input, Key, TextArea};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM, SEED};
|
||||
|
||||
#[inline]
|
||||
fn random_lorem(repeat: usize) -> usize {
|
||||
let mut rng = SmallRng::from_seed(SEED);
|
||||
let mut textarea = TextArea::default();
|
||||
let mut term = dummy_terminal();
|
||||
|
||||
for _ in 0..repeat {
|
||||
for line in LOREM {
|
||||
let row = rng.gen_range(0..textarea.lines().len() as u16);
|
||||
textarea.move_cursor(CursorMove::Jump(row, 0));
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
textarea.input(Input {
|
||||
key: Key::Enter,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
|
||||
for c in line.chars() {
|
||||
textarea.input(Input {
|
||||
key: Key::Char(c),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
});
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.lines().len()
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("insert::random");
|
||||
|
||||
// ~23ms/iter — fits 100 samples in ~5s, keep defaults
|
||||
group.bench_function("1_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(random_lorem(1)))
|
||||
});
|
||||
|
||||
// ~419ms/iter — 20 samples × 419ms ≈ 8s
|
||||
group.sampling_mode(SamplingMode::Flat);
|
||||
group.sample_size(20);
|
||||
group.bench_function("10_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(random_lorem(10)))
|
||||
});
|
||||
|
||||
// ~2.1s/iter — 10 samples × 2.1s ≈ 21s
|
||||
group.sample_size(10);
|
||||
group.bench_function("50_lorem", |b| {
|
||||
b.iter(|| std::hint::black_box(random_lorem(50)))
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(insert_random, bench);
|
||||
criterion_main!(insert_random);
|
||||
37
bench/benches/search_backward.rs
Обычный файл
37
bench/benches/search_backward.rs
Обычный файл
@@ -0,0 +1,37 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::TextArea;
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[inline]
|
||||
fn run(pat: &str, mut textarea: TextArea<'_>) {
|
||||
let mut term = dummy_terminal();
|
||||
textarea.set_search_pattern(pat).unwrap();
|
||||
term.draw_textarea(&textarea);
|
||||
for _ in 0..100 {
|
||||
textarea.search_back(false);
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
textarea.set_search_pattern(r"").unwrap();
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
|
||||
fn short(c: &mut Criterion) {
|
||||
let textarea = TextArea::from(LOREM.iter().map(|s| s.to_string()));
|
||||
c.bench_function("search::back_short", |b| {
|
||||
b.iter(|| run(r"\w*i\w*", textarea.clone()))
|
||||
});
|
||||
}
|
||||
|
||||
fn long(c: &mut Criterion) {
|
||||
let mut lines = vec![];
|
||||
for _ in 0..10 {
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
}
|
||||
let textarea = TextArea::new(lines);
|
||||
c.bench_function("search::back_long", |b| {
|
||||
b.iter(|| run(r"[A-Z]\w*", textarea.clone()))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(search_backward, short, long);
|
||||
criterion_main!(search_backward);
|
||||
@@ -3,16 +3,12 @@ use tui_textarea::TextArea;
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
#[inline]
|
||||
fn run(pat: &str, mut textarea: TextArea<'_>, forward: bool) {
|
||||
fn run(pat: &str, mut textarea: TextArea<'_>) {
|
||||
let mut term = dummy_terminal();
|
||||
textarea.set_search_pattern(pat).unwrap();
|
||||
term.draw_textarea(&textarea);
|
||||
for _ in 0..100 {
|
||||
if forward {
|
||||
textarea.search_forward(false);
|
||||
} else {
|
||||
textarea.search_back(false);
|
||||
}
|
||||
textarea.search_forward(false);
|
||||
term.draw_textarea(&textarea);
|
||||
}
|
||||
textarea.set_search_pattern(r"").unwrap();
|
||||
@@ -22,10 +18,7 @@ fn run(pat: &str, mut textarea: TextArea<'_>, forward: bool) {
|
||||
fn short(c: &mut Criterion) {
|
||||
let textarea = TextArea::from(LOREM.iter().map(|s| s.to_string()));
|
||||
c.bench_function("search::forward_short", |b| {
|
||||
b.iter(|| run(r"\w*i\w*", textarea.clone(), true))
|
||||
});
|
||||
c.bench_function("search::back_short", |b| {
|
||||
b.iter(|| run(r"\w*i\w*", textarea.clone(), false))
|
||||
b.iter(|| run(r"\w*i\w*", textarea.clone()))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,12 +29,9 @@ fn long(c: &mut Criterion) {
|
||||
}
|
||||
let textarea = TextArea::new(lines);
|
||||
c.bench_function("search::forward_long", |b| {
|
||||
b.iter(|| run(r"[A-Z]\w*", textarea.clone(), true))
|
||||
});
|
||||
c.bench_function("search::back_long", |b| {
|
||||
b.iter(|| run(r"[A-Z]\w*", textarea.clone(), false))
|
||||
b.iter(|| run(r"[A-Z]\w*", textarea.clone()))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(search, short, long);
|
||||
criterion_main!(search);
|
||||
criterion_group!(search_forward, short, long);
|
||||
criterion_main!(search_forward);
|
||||
66
bench/benches/undo_redo.rs
Обычный файл
66
bench/benches/undo_redo.rs
Обычный файл
@@ -0,0 +1,66 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
|
||||
use tui_textarea::TextArea;
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt};
|
||||
|
||||
/// Build a textarea with `depth` undo-able edits in history using insert_str,
|
||||
/// interspersed with delete_str to cover the cursor-clamping code path.
|
||||
fn make_history(depth: usize) -> TextArea<'static> {
|
||||
let mut ta = TextArea::default();
|
||||
for i in 0..depth {
|
||||
if i % 4 == 3 {
|
||||
// Every 4th edit is a delete_str — the operation fixed in e6cc8ab.
|
||||
ta.delete_str(3);
|
||||
} else {
|
||||
ta.insert_str("hello ");
|
||||
}
|
||||
}
|
||||
ta
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn undo_all(textarea: &TextArea<'_>) -> usize {
|
||||
let mut t = textarea.clone();
|
||||
let mut term = dummy_terminal();
|
||||
let mut count = 0;
|
||||
while t.undo() {
|
||||
term.draw_textarea(&t);
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn redo_all(textarea: &TextArea<'_>) -> usize {
|
||||
// Start from fully-undone state so redo has work to do.
|
||||
let mut t = textarea.clone();
|
||||
while t.undo() {}
|
||||
let mut term = dummy_terminal();
|
||||
let mut count = 0;
|
||||
while t.redo() {
|
||||
term.draw_textarea(&t);
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("undo_redo");
|
||||
// undo::50 ran Linear with R²=0.19 (Criterion warned "enable flat sampling").
|
||||
// All depths benefit from Flat since history-clone cost varies with depth.
|
||||
group.sampling_mode(SamplingMode::Flat);
|
||||
|
||||
for depth in [50usize, 200, 500] {
|
||||
let ta = make_history(depth);
|
||||
group.bench_function(format!("undo::{}", depth), |b| {
|
||||
b.iter(|| std::hint::black_box(undo_all(&ta)))
|
||||
});
|
||||
group.bench_function(format!("redo::{}", depth), |b| {
|
||||
b.iter(|| std::hint::black_box(redo_all(&ta)))
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(undo_redo, bench);
|
||||
criterion_main!(undo_redo);
|
||||
57
bench/benches/wrap_render.rs
Обычный файл
57
bench/benches/wrap_render.rs
Обычный файл
@@ -0,0 +1,57 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use tui_textarea::{TextArea, WrapMode};
|
||||
use tui_textarea_bench::{dummy_terminal, TerminalExt, LOREM};
|
||||
|
||||
const MODES: &[WrapMode] = &[
|
||||
WrapMode::None,
|
||||
WrapMode::Word,
|
||||
WrapMode::Glyph,
|
||||
WrapMode::WordOrGlyph,
|
||||
];
|
||||
|
||||
fn make_textarea(repeat: usize, mode: WrapMode) -> TextArea<'static> {
|
||||
let mut lines = Vec::with_capacity(LOREM.len() * repeat);
|
||||
for _ in 0..repeat {
|
||||
lines.extend(LOREM.iter().map(|s| s.to_string()));
|
||||
}
|
||||
let mut ta = TextArea::new(lines);
|
||||
ta.set_wrap_mode(mode);
|
||||
ta
|
||||
}
|
||||
|
||||
// Short text: 7 lines, each ~63 chars. DummyBackend width=40 → all lines wrap.
|
||||
fn short(c: &mut Criterion) {
|
||||
for mode in MODES {
|
||||
let ta = make_textarea(1, *mode);
|
||||
let name = format!("wrap_render::short::{:?}", mode);
|
||||
c.bench_function(&name, |b| {
|
||||
b.iter(|| {
|
||||
let mut term = dummy_terminal();
|
||||
for _ in 0..50 {
|
||||
term.draw_textarea(&ta);
|
||||
}
|
||||
std::hint::black_box(())
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Long text: 70 lines, same wrapping pressure.
|
||||
fn long(c: &mut Criterion) {
|
||||
for mode in MODES {
|
||||
let ta = make_textarea(10, *mode);
|
||||
let name = format!("wrap_render::long::{:?}", mode);
|
||||
c.bench_function(&name, |b| {
|
||||
b.iter(|| {
|
||||
let mut term = dummy_terminal();
|
||||
for _ in 0..50 {
|
||||
term.draw_textarea(&ta);
|
||||
}
|
||||
std::hint::black_box(())
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(wrap_render, short, long);
|
||||
criterion_main!(wrap_render);
|
||||
Ссылка в новой задаче
Block a user