Synced from monorepo Changes: - Report invalid MCP server config instead of failing startup - Keep completed terminal output when the gateway connection is lost - Show a duration-only detail view for single-task task output - Don't let a stale registry turn counter hide local sessions - Raise the file-descriptor soft limit on Linux and log effective limits at startup - Stop aborting when HTTP client construction fails - Make session thread and runtime spawn failures recoverable - Fix main-prompt paste parity in the question freeform input - Fire SessionEnd hooks on /exit and headless quit - Embed the deployment-config signing public key - Repaint paste-chip background on inline panel inputs - Security: prevent acceptEdits from auto-approving agent writes into the always-trusted global hook root - Fix stacked "Worked for" markers so parks render as status and turns close with exactly one marker - Parse hooks from config files - Add a remote kill-switch for managed-config signature verification - Security: fix workspace file-reference resolution bypassing workspace filesystem confinement Source-Revision: d02693a856a54f1030695b36b91d276e96b30b23
139 lines
5.3 KiB
Rust
139 lines
5.3 KiB
Rust
//! Fork-path benchmark and profiling workbench.
|
|
//!
|
|
//! Synthesizes a session whose `updates.jsonl` matches a configurable target
|
|
//! size (realistic mixed update shapes: user/agent chunks, tool calls with
|
|
//! large results), then measures `StorageAdapter::copy_session_data` — the
|
|
//! path that materializes the whole file and produced multi-GB RSS spikes on
|
|
//! large production sessions. Also the substrate for allocation/CPU profiling
|
|
//! (`cargo flamegraph --bench fork_copy`, dhat) and future peak-RSS bounds.
|
|
//!
|
|
//! Run: `cargo bench -p xai-grok-shell --bench fork_copy`
|
|
//! Size override: `FORK_BENCH_MB=64 cargo bench ...` (default 16 MB).
|
|
|
|
use std::hint::black_box;
|
|
use std::time::Duration;
|
|
|
|
use acp::{ContentBlock, ContentChunk, TextContent};
|
|
use agent_client_protocol as acp;
|
|
use criterion::{
|
|
BenchmarkId, Criterion, SamplingMode, Throughput, criterion_group, criterion_main,
|
|
};
|
|
use tempfile::TempDir;
|
|
use xai_grok_shell::session::info::Info;
|
|
use xai_grok_shell::session::storage::{
|
|
CopySessionOptions, JsonlStorageAdapter, SessionUpdate, StorageAdapter,
|
|
};
|
|
|
|
/// One synthetic "turn": a user chunk, agent chunks, and a bulky tool result,
|
|
/// so line-size distribution and parse cost resemble production sessions.
|
|
fn turn_updates(info: &Info, turn: usize) -> Vec<SessionUpdate> {
|
|
let text = |s: String| ContentChunk::new(ContentBlock::Text(TextContent::new(s)));
|
|
let notify =
|
|
|u| SessionUpdate::Acp(Box::new(acp::SessionNotification::new(info.id.clone(), u)));
|
|
let mut updates = vec![notify(acp::SessionUpdate::UserMessageChunk(text(format!(
|
|
"prompt {turn}: check the build and summarize failures"
|
|
))))];
|
|
for i in 0..8 {
|
|
updates.push(notify(acp::SessionUpdate::AgentMessageChunk(text(format!(
|
|
"agent chunk {turn}/{i}: analyzing module {i} for regressions and drafting a fix plan"
|
|
)))));
|
|
}
|
|
// ~4 KB tool-result payload: the dominant byte source in real sessions.
|
|
updates.push(notify(acp::SessionUpdate::AgentMessageChunk(text(
|
|
format!("tool result {turn}: {}", "x".repeat(4096)),
|
|
))));
|
|
updates
|
|
}
|
|
|
|
/// Build a session dir whose `updates.jsonl` is at least `target_bytes`.
|
|
fn synthesize_session(root: &TempDir, target_bytes: u64) -> Info {
|
|
let adapter = JsonlStorageAdapter::with_root(root.path().to_path_buf());
|
|
let info = Info {
|
|
id: acp::SessionId::new("fork-bench-src"),
|
|
cwd: "/bench/workspace".to_string(),
|
|
};
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("bench runtime");
|
|
rt.block_on(async {
|
|
adapter
|
|
.init_session(&info, acp::ModelId::new("bench-model"))
|
|
.await
|
|
.expect("init session");
|
|
let updates_path = adapter.updates_file_path(&info).expect("updates path");
|
|
let mut turn = 0usize;
|
|
loop {
|
|
for update in turn_updates(&info, turn) {
|
|
adapter.append_update(&info, &update).await.expect("append");
|
|
}
|
|
turn += 1;
|
|
// Stat every 32 turns; sizes only grow.
|
|
if turn % 32 == 0
|
|
&& std::fs::metadata(&updates_path)
|
|
.map(|m| m.len())
|
|
.unwrap_or(0)
|
|
>= target_bytes
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
info
|
|
}
|
|
|
|
fn bench_fork_copy(c: &mut Criterion) {
|
|
let target_mb: u64 = std::env::var("FORK_BENCH_MB")
|
|
.ok()
|
|
.and_then(|v| v.parse().ok())
|
|
.unwrap_or(16);
|
|
let root = TempDir::new().expect("tempdir");
|
|
let source = synthesize_session(&root, target_mb * 1024 * 1024);
|
|
let adapter = JsonlStorageAdapter::with_root(root.path().to_path_buf());
|
|
let updates_len = std::fs::metadata(adapter.updates_file_path(&source).expect("updates path"))
|
|
.expect("updates.jsonl")
|
|
.len();
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("bench runtime");
|
|
|
|
let mut group = c.benchmark_group("fork_copy");
|
|
group
|
|
.sampling_mode(SamplingMode::Flat)
|
|
.sample_size(10)
|
|
.measurement_time(Duration::from_secs(30))
|
|
.throughput(Throughput::Bytes(updates_len));
|
|
group.bench_function(
|
|
BenchmarkId::new("copy_session_data", format!("{target_mb}MB")),
|
|
|b| {
|
|
let mut n = 0usize;
|
|
b.iter(|| {
|
|
n += 1;
|
|
let target = Info {
|
|
id: acp::SessionId::new(format!("fork-bench-dst-{n}")),
|
|
cwd: "/bench/workspace-fork".to_string(),
|
|
};
|
|
let result = rt
|
|
.block_on(adapter.copy_session_data(
|
|
&source,
|
|
&target,
|
|
CopySessionOptions::default(),
|
|
))
|
|
.expect("fork copy");
|
|
// Keep each iteration's output dir from accumulating.
|
|
if let Some(dir) = adapter
|
|
.updates_file_path(&target)
|
|
.and_then(|p| p.parent().map(std::path::Path::to_path_buf))
|
|
{
|
|
std::fs::remove_dir_all(&dir).ok();
|
|
}
|
|
black_box(result)
|
|
});
|
|
},
|
|
);
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, bench_fork_copy);
|
|
criterion_main!(benches);
|