pmacs/tests/m10_10_perf.rs

140 lines
5.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! T M10.10 Day 3 step 7 — perf measurement baseline.
//!
//! Measures `Buffer::apply_remote_crdt_op` cost across buffer sizes
//! (1KB / 100KB / 1MB). Under Path β's narrow optimistic-paint
//! scope, M10.10 doesn't have a tight perf requirement — the
//! measurements are baseline numbers for v0.2+ optimization work:
//!
//! - **v0.2+ Path γ** (layout-aware optimistic paint) needs
//! before-state to compare against.
//! - **v0.x A1 mitigation** (unicode-method path from M10.2's 391×
//! finding) needs before-state to verify improvement.
//!
//! The numbers are recorded to stdout via eprintln (visible under
//! `cargo test -- --nocapture`) and asserted against generous bounds
//! that exist to catch catastrophic regressions, not to verify a
//! tight perf claim.
#![cfg(feature = "crdt")]
use std::time::Instant;
use pmacs::buffer::{Buffer, BufferId};
use pmacs::crdt::CrdtState;
/// Build a buffer of approximately `size` bytes seeded with ASCII
/// content, CRDT-upgraded under `peer_id` 1 (the LOCAL daemon peer).
/// Returns the buffer plus a remote-peer state synced with it for
/// generating ops.
fn build_buffer_with_size(size: usize) -> (Buffer, CrdtState) {
// Seed text: 'a' repeated. Same content via both paths so the
// buffer and donor CrdtState are byte-equivalent.
let content: Vec<u8> = std::iter::repeat_n(b'a', size).collect();
let buf = Buffer::from_bytes_with_crdt(BufferId::next(), "*perf*", &content, 1)
.expect("buf from bytes with crdt");
// Synchronize a donor (simulating a remote peer) to the buffer's
// CRDT state via snapshot. Donor uses a distinct peer_id so the
// ops it produces are attributable to a different peer.
let donor_snap = buf
.crdt_state()
.expect("buf crdt")
.export_snapshot()
.expect("snap");
let donor = CrdtState::new(2).expect("donor");
donor.import_snapshot(&donor_snap).expect("donor import");
(buf, donor)
}
/// Measure `apply_remote_crdt_op` for a buffer of `size` bytes
/// receiving a single 1-byte insertion at position 0 from a remote
/// peer. Returns elapsed time.
fn measure_apply_remote(size: usize) -> std::time::Duration {
let (mut buf, donor) = build_buffer_with_size(size);
// Donor produces a small op (insert one char at position 0).
let v_before = donor.version();
donor.insert(0, "X").expect("donor edit");
let op_bytes = donor.export_updates_since(&v_before).expect("export");
let start = Instant::now();
let _edit = buf.apply_remote_crdt_op(&op_bytes).expect("apply remote");
start.elapsed()
}
#[test]
fn m10_10_apply_remote_crdt_op_at_1kb() {
let elapsed = measure_apply_remote(1024);
let us = elapsed.as_micros();
eprintln!("[M10.10 perf] apply_remote_crdt_op at 1 KB: {us}µs");
// Generous bound: 1KB should never exceed 10ms even on slow CI.
// Tight bound is recorded in audit, not asserted.
assert!(
elapsed < std::time::Duration::from_millis(10),
"apply_remote_crdt_op at 1KB took {us}µs; expected sub-10ms"
);
}
#[test]
fn m10_10_apply_remote_crdt_op_at_100kb() {
let elapsed = measure_apply_remote(100 * 1024);
let us = elapsed.as_micros();
eprintln!("[M10.10 perf] apply_remote_crdt_op at 100 KB: {us}µs");
// Generous bound: 100KB on CI should complete in under 200ms.
// The audit records the actual measurement; this assertion is a
// catastrophic-regression guard.
assert!(
elapsed < std::time::Duration::from_millis(200),
"apply_remote_crdt_op at 100KB took {us}µs; expected sub-200ms"
);
}
#[test]
fn m10_10_apply_remote_crdt_op_at_1mb() {
// The 1MB case stresses the path. Under M10.2's 391× finding,
// byte-native loro operations on multi-MB buffers can take tens
// of ms per op (the unicode-method mitigation closes this gap).
// M10.10's perf measurement records the before-state.
let elapsed = measure_apply_remote(1024 * 1024);
let ms = elapsed.as_millis();
eprintln!("[M10.10 perf] apply_remote_crdt_op at 1 MB: {ms}ms");
// Very generous bound: 1MB on CI should complete in under 5s.
// If we exceed this, something is structurally wrong (not just
// the M10.2 391× pattern).
assert!(
elapsed < std::time::Duration::from_secs(5),
"apply_remote_crdt_op at 1MB took {ms}ms; expected sub-5s"
);
}
/// Records buffer-size scaling in one test for the audit's perf
/// table. Runs three measurements and prints them together so
/// `cargo test -- --nocapture` shows the scaling pattern at a
/// glance.
#[test]
fn m10_10_apply_remote_crdt_op_scaling_report() {
let sizes = [1024usize, 100 * 1024, 1024 * 1024];
eprintln!("\n[M10.10 perf scaling]");
eprintln!(" size | apply_remote_crdt_op");
eprintln!(" --------|---------------------");
for size in sizes {
// Run three iterations and report the median; smooths out
// outliers from cold-start and noisy CI runners.
let mut samples: Vec<_> = (0..3).map(|_| measure_apply_remote(size)).collect();
samples.sort();
let median = samples[1];
let label = match size {
n if n < 10 * 1024 => format!("{n} B"),
n if n < 10 * 1024 * 1024 => format!("{} KB", n / 1024),
n => format!("{} MB", n / (1024 * 1024)),
};
eprintln!(
" {label:7} | {} µs ({} ms)",
median.as_micros(),
median.as_millis()
);
}
eprintln!();
}