perf: fast-path identical snapshots in diff_snapshots (#922)

* Update agent-browser optimization plan: PR #916 submitted, Phase 2 complete

Co-authored-by: Hermes <agent@hermes.ai>

* perf(snapshot-diff): fast-path identical snapshots

Co-authored-by: Hermes <agent@hermes.ai>

* chore(pr): remove unrelated docs from snapshot-diff fast-path

---------

Co-authored-by: Merlin <merlin@rbeckner.com>
Co-authored-by: Hermes <agent@hermes.ai>
This commit is contained in:
Merlin
2026-03-19 20:02:43 -05:00
committed by GitHub
co-authored by Hermes Merlin
parent af800f8403
commit 9837b9c1aa
+79
View File
@@ -101,6 +101,21 @@ pub fn diff_screenshot(
/// Compute a snapshot diff using the Myers algorithm via the `similar` crate.
pub fn diff_snapshots(before: &str, after: &str) -> SnapshotDiffResult {
// Fast path: identical inputs.
// This avoids constructing the `similar` TextDiff object and running the diff
// iteration when agents compare a snapshot to itself (common in retry/loop
// workloads).
if before == after {
let unchanged = before.lines().count();
return SnapshotDiffResult {
diff: String::new(),
additions: 0,
removals: 0,
unchanged,
changed: false,
};
}
let text_diff = TextDiff::from_lines(before, after);
let mut additions = 0usize;
@@ -192,4 +207,68 @@ mod tests {
assert_eq!(result.unchanged, 1);
assert!(!result.diff.is_empty());
}
#[test]
fn test_diff_snapshots_identical_fast_path() {
let input = "hello\nworld\n";
let result = diff_snapshots(input, input);
assert!(!result.changed);
assert_eq!(result.additions, 0);
assert_eq!(result.removals, 0);
assert_eq!(result.unchanged, input.lines().count());
assert!(result.diff.is_empty());
}
#[test]
#[ignore]
fn bench_diff_snapshots_identical_and_changed() {
use std::hint::black_box;
use std::time::Instant;
let identical_a = (0..200)
.map(|i| format!("line {i}"))
.collect::<Vec<_>>()
.join("\n");
let identical_b = identical_a.clone();
let changed_a = identical_a.clone();
let changed_b = (0..200)
.map(|i| {
if i == 123 {
format!("line {i} changed")
} else {
format!("line {i}")
}
})
.collect::<Vec<_>>()
.join("\n");
// Keep the iteration count high enough to measure, but low enough
// to avoid long CI times when someone runs `--ignored`.
let iters = 50_000usize;
let start = Instant::now();
let mut acc_changed = 0usize;
for _ in 0..iters {
let r = diff_snapshots(black_box(&identical_a), black_box(&identical_b));
acc_changed ^= r.unchanged;
}
let identical_ms = start.elapsed().as_secs_f64() * 1000.0;
let start = Instant::now();
let mut acc_changed2 = 0usize;
for _ in 0..iters {
let r = diff_snapshots(black_box(&changed_a), black_box(&changed_b));
acc_changed2 ^= r.additions;
}
let changed_ms = start.elapsed().as_secs_f64() * 1000.0;
// Prevent the compiler from optimizing everything away.
black_box(acc_changed);
black_box(acc_changed2);
println!(
"bench_diff_snapshots_identical_and_changed: iters={iters} identical_ms={identical_ms:.2} changed_ms={changed_ms:.2}"
);
}
}