Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions crates/mudling-bench/src/line_pipeline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! The line pipeline's share of the §6 "one server line ≤ 20 µs" budget:
//! each replayed line goes through split, decode and ANSI strip on its own,
//! so every line gets its own timing.

use mudling_core::line::LinePipeline;
use mudling_core::stats::Summary;
use serde::Serialize;
use std::time::Instant;

/// Leaves the rest of the 20 µs line budget to plugins.
const BUDGET_US: f64 = 5.0;
const MIN_LINES: usize = 100_000;

#[derive(Serialize)]
pub struct Report {
lines: usize,
per_line: Summary,
budget_us: f64,
pub within_budget: bool,
}

/// Replays `source` (at least [`MIN_LINES`] lines, starting over as needed),
/// each line ended as Diku-family servers end them, with `\n\r`.
pub fn run(source: &[String]) -> Report {
let count = source.len().max(MIN_LINES);
let mut pipeline = LinePipeline::default();
let mut lines = Vec::with_capacity(1);
let mut bytes = Vec::new();
let mut samples = Vec::with_capacity(count);
for raw in source.iter().cycle().take(count) {
bytes.clear();
bytes.extend_from_slice(raw.as_bytes());
bytes.extend_from_slice(b"\n\r");
let started = Instant::now();
pipeline.push(&bytes, &mut lines);
samples.push(started.elapsed());
lines.clear();
}
let per_line = Summary::of(&samples).expect("lines were timed");
Report {
lines: count,
within_budget: per_line.p99_us <= BUDGET_US,
per_line,
budget_us: BUDGET_US,
}
}

pub fn print(report: &Report) {
println!(
"line pipeline over {} lines (split + decode + ANSI strip): p50 {:.2} µs, p99 {:.2} µs, max {:.1} µs; budget p99 ≤ {} µs: {}",
report.lines,
report.per_line.p50_us,
report.per_line.p99_us,
report.per_line.max_us,
report.budget_us,
if report.within_budget { "ok" } else { "MISSED" }
);
}
17 changes: 14 additions & 3 deletions crates/mudling-bench/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
//! one parses a raw line and evicts the oldest), and finds over the full
//! buffer: every match (the budget), and the first page of matches.
//! `--check` exits non-zero when a budget is missed.
//! It also times the line pipeline per line over the same text.

mod line_pipeline;

use mudling_core::replay::{self, ReplayFeed};
use mudling_core::stats::Summary;
Expand Down Expand Up @@ -80,6 +83,7 @@ struct Report {
find_budget_us: f64,
append_within_budget: bool,
find_within_budget: bool,
line_pipeline: line_pipeline::Report,
}

fn main() -> ExitCode {
Expand Down Expand Up @@ -111,16 +115,22 @@ fn main() -> ExitCode {
}
};

let report = run(source, ReplayFeed::new(lines));
let line_pipeline = line_pipeline::run(&lines);
let report = run(source, ReplayFeed::new(lines), line_pipeline);
print_report(&report);
line_pipeline::print(&report.line_pipeline);
if let Some(path) = &options.json {
let json = serde_json::to_string_pretty(&report).expect("the report serializes");
if let Err(error) = std::fs::write(path, json) {
eprintln!("couldn't write {path}: {error}");
return ExitCode::from(2);
}
}
if options.check && !(report.append_within_budget && report.find_within_budget) {
if options.check
&& !(report.append_within_budget
&& report.find_within_budget
&& report.line_pipeline.within_budget)
{
eprintln!("a budget was missed");
return ExitCode::FAILURE;
}
Expand All @@ -145,7 +155,7 @@ fn parse_options() -> Result<Options, String> {
Ok(options)
}

fn run(source: String, mut feed: ReplayFeed) -> Report {
fn run(source: String, mut feed: ReplayFeed, line_pipeline: line_pipeline::Report) -> Report {
let mut parser = AnsiParser::default();
let heap_before = HEAP_BYTES.load(Ordering::Relaxed);
let fill_started = Instant::now();
Expand Down Expand Up @@ -197,6 +207,7 @@ fn run(source: String, mut feed: ReplayFeed) -> Report {
find_matches,
append_budget_us: APPEND_BUDGET_US,
find_budget_us: FIND_BUDGET_US,
line_pipeline,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/mudling-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ publish.workspace = true

[dependencies]
aho-corasick = "1.1"
memchr = "2.7"
serde.workspace = true

[lints]
Expand Down
Loading
Loading