use super::*;
use crate::sessions::Session;
fn append_completed_turn(session: &Session, cwd: &Path, user: &str, assistant: &str) {
for (kind, payload) in [
(SessionEventKind::UserInput, json!({"text": user})),
(
SessionEventKind::AssistantOutput,
json!({"text": assistant}),
),
(SessionEventKind::TurnStatus, json!({"status": "completed"})),
] {
session
.append(&SessionEvent::new_kind(
kind,
session.id().to_string(),
cwd.to_path_buf(),
payload,
))
.unwrap();
}
}
fn add_metrics(total: &mut ReplayCacheMetrics, next: ReplayCacheMetrics) {
total.full_scans += next.full_scans;
total.bytes_read += next.bytes_read;
total.lines_parsed += next.lines_parsed;
total.events_parsed += next.events_parsed;
total.replay_builds += next.replay_builds;
total.events_before_cutoff += next.events_before_cutoff;
total.events_after_cutoff += next.events_after_cutoff;
total.cache_hits += next.cache_hits;
total.invalidations += next.invalidations;
}
fn replay_benchmark_sample() -> (
std::time::Duration,
std::time::Duration,
ReplayCacheMetrics,
ReplayCacheMetrics,
) {
let temp = TempDir::new().unwrap();
let session = crate::sessions::SessionManager::new(temp.path().join("sessions"))
.create()
.unwrap();
let mut baseline_elapsed = std::time::Duration::ZERO;
let mut optimized_elapsed = std::time::Duration::ZERO;
let mut baseline_metrics = ReplayCacheMetrics::default();
let mut optimized = ConversationReplayCache::default();
const PROMPTS: usize = 40;
for index in 0..PROMPTS {
append_completed_turn(
&session,
temp.path(),
&format!("question {index} {}", "x".repeat(2_000)),
&format!("answer {index} {}", "y".repeat(2_000)),
);
let start = std::time::Instant::now();
for _ in 0..2 {
let mut replay = ConversationReplayCache::default();
replay.replay(Some(&session)).unwrap();
add_metrics(&mut baseline_metrics, replay.metrics());
}
baseline_elapsed += start.elapsed();
let start = std::time::Instant::now();
optimized.replay(Some(&session)).unwrap();
optimized.replay(Some(&session)).unwrap();
optimized_elapsed += start.elapsed();
}
(
baseline_elapsed,
optimized_elapsed,
baseline_metrics,
optimized.metrics(),
)
}
fn percentile(samples: &mut [std::time::Duration], percentile: usize) -> std::time::Duration {
samples.sort_unstable();
let index = (samples.len() - 1) * percentile / 100;
samples[index]
}
#[test]
#[ignore = "release-mode replay benchmark; run with --release --ignored --nocapture"]
fn replay_snapshot_benchmark_growing_sessions() {
const WARMUP_SAMPLES: usize = 1;
const SAMPLES: usize = 9;
const PROMPTS: u64 = 40;
for _ in 0..WARMUP_SAMPLES {
let _ = replay_benchmark_sample();
}
let mut baseline_times = Vec::with_capacity(SAMPLES);
let mut optimized_times = Vec::with_capacity(SAMPLES);
let mut baseline_metrics = ReplayCacheMetrics::default();
let mut optimized_metrics = ReplayCacheMetrics::default();
for _ in 0..SAMPLES {
let (baseline, optimized, baseline_sample, optimized_sample) = replay_benchmark_sample();
baseline_times.push(baseline);
optimized_times.push(optimized);
baseline_metrics = baseline_sample;
optimized_metrics = optimized_sample;
}
let baseline_median = percentile(&mut baseline_times.clone(), 50);
let baseline_p95 = percentile(&mut baseline_times, 95);
let optimized_median = percentile(&mut optimized_times.clone(), 50);
let optimized_p95 = percentile(&mut optimized_times, 95);
eprintln!(
"session_replay_benchmark fixture_seed=sequential-0.70 warmup={WARMUP_SAMPLES} samples={SAMPLES} prompts={PROMPTS} baseline_median_ms={} baseline_p95_ms={} optimized_median_ms={} optimized_p95_ms={} baseline_scans={} optimized_scans={} baseline_bytes={} optimized_bytes={} baseline_lines={} optimized_lines={} baseline_events={} optimized_events={} baseline_replay_builds={} optimized_replay_builds={} optimized_cache_hits={}",
baseline_median.as_millis(),
baseline_p95.as_millis(),
optimized_median.as_millis(),
optimized_p95.as_millis(),
baseline_metrics.full_scans,
optimized_metrics.full_scans,
baseline_metrics.bytes_read,
optimized_metrics.bytes_read,
baseline_metrics.lines_parsed,
optimized_metrics.lines_parsed,
baseline_metrics.events_parsed,
optimized_metrics.events_parsed,
baseline_metrics.replay_builds,
optimized_metrics.replay_builds,
optimized_metrics.cache_hits,
);
assert_eq!(baseline_metrics.full_scans, PROMPTS * 2);
assert_eq!(optimized_metrics.full_scans, PROMPTS);
assert_eq!(optimized_metrics.replay_builds, PROMPTS);
assert_eq!(optimized_metrics.cache_hits, PROMPTS);
assert!(optimized_median * 4 <= baseline_median * 3);
}