-
Notifications
You must be signed in to change notification settings - Fork 0
bench: add benchmarks and results #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ade05b6
1f89be3
13d0eda
a2a379e
30b5c4a
cb9bee5
da9be04
3f6146b
f5e7549
ca93584
71494a9
30ac3ee
d633aeb
6bb4595
29fa662
87a967e
a8ed2dc
c36a4cb
59ee779
cef5922
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,103 @@ | ||
| fn main() {} | ||
| use std::hint::black_box; | ||
|
|
||
| use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; | ||
| use fasrt::srt::Parser; | ||
|
|
||
| const SMALL_SRT: &str = "\ | ||
| 1 | ||
| 00:00:01,000 --> 00:00:04,000 | ||
| Hello world! | ||
|
|
||
| 2 | ||
| 00:00:05,000 --> 00:00:08,000 | ||
| Goodbye world! | ||
| "; | ||
|
|
||
| const MEDIUM_SRT: &str = include_str!("../fixtures/srt/DeathNote_01.eng.srt"); | ||
|
al8n marked this conversation as resolved.
|
||
|
|
||
|
Comment on lines
+14
to
+17
|
||
| fn load_all_fixtures() -> String { | ||
| if let Ok(read_dir) = std::fs::read_dir("fixtures/srt") { | ||
| let mut paths: Vec<_> = read_dir | ||
| .filter_map(|entry| { | ||
| let entry = entry.ok()?; | ||
| let path = entry.path(); | ||
| if path.extension().is_some_and(|e| e == "srt") { | ||
| Some(path) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .collect(); | ||
|
|
||
| paths.sort(); | ||
|
|
||
| let mut buf = String::new(); | ||
| for path in paths { | ||
| if let Ok(content) = std::fs::read_to_string(path) { | ||
| if !buf.is_empty() { | ||
| buf.push_str("\n\n"); | ||
| } | ||
| buf.push_str(&content); | ||
| } | ||
| } | ||
| buf | ||
| } else { | ||
| // Fallback: use a small embedded sample when fixtures are unavailable. | ||
| SMALL_SRT.to_string() | ||
| } | ||
| } | ||
|
|
||
| fn bench_srt_parse(c: &mut Criterion) { | ||
| let all_fixtures = load_all_fixtures(); | ||
|
|
||
| let mut group = c.benchmark_group("srt/parse"); | ||
|
|
||
| // Small inline SRT | ||
| group.throughput(Throughput::Bytes(SMALL_SRT.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("strict", "small_2_cues"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::strict(black_box(SMALL_SRT)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| // Medium real file (~26 KB) | ||
| group.throughput(Throughput::Bytes(MEDIUM_SRT.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("strict", "medium_26kb"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::strict(black_box(MEDIUM_SRT)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| // All fixtures (~8 MB) | ||
| group.throughput(Throughput::Bytes(all_fixtures.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("lossy", "all_fixtures_8mb"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::lossy(black_box(&all_fixtures)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| fn bench_srt_collect(c: &mut Criterion) { | ||
| let mut group = c.benchmark_group("srt/collect"); | ||
|
|
||
| // Collect into Vec to measure allocation overhead | ||
| group.throughput(Throughput::Bytes(MEDIUM_SRT.len() as u64)); | ||
| group.bench_function("medium_26kb", |b| { | ||
| b.iter(|| { | ||
| let entries: Vec<_> = Parser::strict(black_box(MEDIUM_SRT)) | ||
| .collect::<Result<_, _>>() | ||
| .unwrap(); | ||
| black_box(entries.len()); | ||
| }); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| criterion_group!(benches, bench_srt_parse, bench_srt_collect); | ||
| criterion_main!(benches); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| use std::hint::black_box; | ||
|
|
||
| use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; | ||
| use fasrt::vtt::Parser; | ||
| use fasrt::vtt::cue::CueParser; | ||
|
|
||
| const SMALL_VTT: &str = "\ | ||
| WEBVTT | ||
|
|
||
| 00:00:00.000 --> 00:00:01.000 | ||
| Hello world! | ||
|
|
||
| 00:00:01.000 --> 00:00:02.000 | ||
| Goodbye world! | ||
| "; | ||
|
|
||
| const SETTINGS_VTT: &str = "\ | ||
| WEBVTT | ||
|
|
||
| STYLE | ||
| ::cue { color: white; } | ||
|
|
||
| REGION | ||
| id:region1 | ||
| width:40% | ||
| lines:3 | ||
| regionanchor:0%,100% | ||
| viewportanchor:10%,90% | ||
| scroll:up | ||
|
|
||
| cue-1 | ||
| 00:00:00.000 --> 00:00:01.000 align:start position:10% size:80% line:0 vertical:rl region:region1 | ||
| <b>Bold</b> and <i>italic</i> text | ||
|
|
||
| NOTE This is a comment | ||
|
|
||
| 00:00:01.000 --> 00:00:05.000 | ||
| Second cue with <v Roger Bingham>voice tag</v> | ||
| "; | ||
|
|
||
| fn load_all_vtt_fixtures() -> String { | ||
| let mut buf = String::new(); | ||
| for dir in &[ | ||
| "fixtures/webvtt/wpt-file-parsing", | ||
| "fixtures/webvtt/wpt-cue-parsing", | ||
| ] { | ||
| if let Ok(read_dir) = std::fs::read_dir(dir) { | ||
| let mut entries: Vec<_> = read_dir.filter_map(Result::ok).collect(); | ||
| entries.sort_by(|a, b| a.file_name().cmp(&b.file_name())); | ||
| for entry in entries { | ||
| if entry.path().extension().is_some_and(|e| e == "vtt") { | ||
| if let Ok(contents) = std::fs::read_to_string(entry.path()) { | ||
| buf.push_str(&contents); | ||
| buf.push_str("\n\n"); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if buf.is_empty() { | ||
| // Fallback to embedded samples so benches still run without fixtures. | ||
| buf.push_str(SMALL_VTT); | ||
| buf.push_str("\n\n"); | ||
| buf.push_str(SETTINGS_VTT); | ||
| } | ||
| buf | ||
| } | ||
|
|
||
| fn bench_vtt_parse(c: &mut Criterion) { | ||
| let all_fixtures = load_all_vtt_fixtures(); | ||
|
|
||
| let mut group = c.benchmark_group("vtt/parse"); | ||
|
|
||
| // Small inline VTT | ||
| group.throughput(Throughput::Bytes(SMALL_VTT.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("parse", "small_2_cues"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::new(black_box(SMALL_VTT)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| // VTT with settings, regions, styles, cue options | ||
| group.throughput(Throughput::Bytes(SETTINGS_VTT.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("parse", "with_settings"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::new(black_box(SETTINGS_VTT)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| // All VTT fixtures | ||
| group.throughput(Throughput::Bytes(all_fixtures.len() as u64)); | ||
| group.bench_function(BenchmarkId::new("parse", "all_fixtures"), |b| { | ||
| b.iter(|| { | ||
| let count = Parser::new(black_box(&all_fixtures)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| fn bench_vtt_collect(c: &mut Criterion) { | ||
| let mut group = c.benchmark_group("vtt/collect"); | ||
|
|
||
| group.throughput(Throughput::Bytes(SETTINGS_VTT.len() as u64)); | ||
| group.bench_function("with_settings", |b| { | ||
| b.iter(|| { | ||
| let blocks: Vec<_> = Parser::new(black_box(SETTINGS_VTT)) | ||
| .collect::<Result<_, _>>() | ||
| .unwrap(); | ||
| black_box(blocks.len()); | ||
| }); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| /// Cue text with many timestamp tags to benchmark the cue-text parsing path. | ||
| fn build_cue_text_with_timestamps() -> String { | ||
| let mut s = String::new(); | ||
| for i in 0..500 { | ||
| let h = i / 3600; | ||
| let m = (i % 3600) / 60; | ||
| let sec = i % 60; | ||
| s.push_str(&format!( | ||
| "word <{h:02}:{m:02}:{sec:02}.{ms:03}>text", | ||
| h = h, | ||
| m = m, | ||
| sec = sec, | ||
| ms = (i * 7) % 1000 | ||
| )); | ||
| } | ||
| s | ||
| } | ||
|
|
||
| /// Cue text with tags but no timestamps. | ||
| const CUE_TEXT_TAGS: &str = "\ | ||
| <b>bold <i>bold-italic</i></b> plain <u>underline</u> \ | ||
| <v Roger>voice</v> <lang en>english</lang> <ruby>base<rt>ruby</rt></ruby> \ | ||
| <c.highlight.big>classed</c> & < > end"; | ||
|
|
||
| fn bench_vtt_cue_text(c: &mut Criterion) { | ||
| let ts_input = build_cue_text_with_timestamps(); | ||
|
|
||
| let mut group = c.benchmark_group("vtt/cue_text"); | ||
|
|
||
| // Tags only (no timestamps) | ||
| group.throughput(Throughput::Bytes(CUE_TEXT_TAGS.len() as u64)); | ||
| group.bench_function("tags_only", |b| { | ||
| b.iter(|| { | ||
| let count = CueParser::new(black_box(CUE_TEXT_TAGS)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| // Timestamp-heavy cue text | ||
| group.throughput(Throughput::Bytes(ts_input.len() as u64)); | ||
| group.bench_function("500_timestamps", |b| { | ||
| b.iter(|| { | ||
| let count = CueParser::new(black_box(&ts_input)).count(); | ||
| black_box(count); | ||
| }); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| criterion_group!( | ||
| benches, | ||
| bench_vtt_parse, | ||
| bench_vtt_collect, | ||
| bench_vtt_cue_text | ||
| ); | ||
| criterion_main!(benches); |
Uh oh!
There was an error while loading. Please reload this page.