1
0
зеркало из 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
Этот коммит содержится в:
srothgan
2026-02-19 00:22:09 +01:00
коммит произвёл GitHub
родитель f03a7b9ec1
Коммит 19151a4076
20 изменённых файлов: 821 добавлений и 351 удалений

Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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);