Measure cached Dosh attach fast path
ci / test (push) Has been cancelled
ci / remote-bench (push) Has been cancelled

This commit is contained in:
DuProcess
2026-06-12 22:45:13 -04:00
parent 3a38a9da80
commit 93374162ff
4 changed files with 94 additions and 38 deletions
+5 -3
View File
@@ -201,9 +201,11 @@ make bench-docker-mosh
``` ```
That prints `ssh_true_ms`, `dosh_attach_ms`, and `mosh_start_true_ms` under the same That prints `ssh_true_ms`, `dosh_attach_ms`, and `mosh_start_true_ms` under the same
container, key, DNS, and network path. See `docs/PUBLIC_READINESS.md` before using container, key, DNS, and network path. It also prints `dosh_cached_attach_ms`, which
the numbers publicly; Dosh's current strongest claim is fast attach/reconnect, not is the real Dosh fast path after the first SSH-authenticated bootstrap has issued an
full Mosh feature parity yet. attach ticket. See `docs/PUBLIC_READINESS.md` before using the numbers publicly;
Dosh's current strongest claim is fast attach/reconnect, not full Mosh feature
parity yet.
The CI workflow includes an optional remote benchmark job. It runs when The CI workflow includes an optional remote benchmark job. It runs when
`DOSH_BENCH_HOST`, `DOSH_BENCH_USER`, and `DOSH_BENCH_SSH_KEY` repository secrets are `DOSH_BENCH_HOST`, `DOSH_BENCH_USER`, and `DOSH_BENCH_SSH_KEY` repository secrets are
+7 -1
View File
@@ -27,13 +27,19 @@ Reported metrics:
| Metric | Meaning | | Metric | Meaning |
| --- | --- | | --- | --- |
| `ssh_true_ms` | Time to run `ssh host true` with the same key/options. | | `ssh_true_ms` | Time to run `ssh host true` with the same key/options. |
| `dosh_attach_ms` | Time for `dosh-client --attach-only` to render the first frame and detach. | | `dosh_attach_ms` | Time for `dosh-client --attach-only` to render the first frame and detach on the configured path. With `--no-cache`, this is cold SSH bootstrap plus UDP attach. |
| `dosh_cached_attach_ms` | Time for a warmed Dosh client to attach using cached UDP credentials/tickets, render the first frame, and detach. This is the fast-path claim and should be approximately network RTT plus local process/render overhead. |
| `mosh_start_true_ms` | Time for `mosh host -- true` to bootstrap, run `true`, and exit. | | `mosh_start_true_ms` | Time for `mosh host -- true` to bootstrap, run `true`, and exit. |
These are not identical workloads. They are still useful because they measure the These are not identical workloads. They are still useful because they measure the
startup path each tool must pay before useful remote work begins. Public numbers startup path each tool must pay before useful remote work begins. Public numbers
must include the command, machine, OS, CPU, network path, and sample count. must include the command, machine, OS, CPU, network path, and sample count.
Do not cite cold `dosh_attach_ms` as the core speed claim. Cold Dosh still pays SSH
startup/authentication. Cite `dosh_cached_attach_ms` for repeat attach/reconnect
speed, and cite cold `dosh_attach_ms` only to show that fallback remains competitive
with ordinary SSH.
## Feature Matrix ## Feature Matrix
| Feature | Mosh | Dosh now | Public status | | Feature | Mosh | Dosh now | Public status |
+17 -1
View File
@@ -31,7 +31,7 @@ for _ in 1 2 3 4 5; do
done done
ssh-keyscan -p "$ssh_port" 127.0.0.1 > "$workdir/known_hosts" ssh-keyscan -p "$ssh_port" 127.0.0.1 > "$workdir/known_hosts"
mkdir -p "$workdir/home" "$workdir/home-controlmaster" "$workdir/home-mosh" mkdir -p "$workdir/home" "$workdir/home-controlmaster" "$workdir/home-cached" "$workdir/home-mosh"
if ! HOME="$workdir/home" target/release/dosh-bench \ if ! HOME="$workdir/home" target/release/dosh-bench \
--server bench@127.0.0.1 \ --server bench@127.0.0.1 \
@@ -64,6 +64,22 @@ if ! HOME="$workdir/home-controlmaster" target/release/dosh-bench \
exit 1 exit 1
fi fi
if ! HOME="$workdir/home-cached" target/release/dosh-bench \
--server bench@127.0.0.1 \
--ssh-port "$ssh_port" \
--dosh-port "$dosh_port" \
--ssh-key "$workdir/id_ed25519" \
--ssh-known-hosts "$workdir/known_hosts" \
--ssh-auth-command /usr/local/bin/dosh-auth \
--iterations 10 \
--warm-cache \
--skip-ssh-baseline \
--assert-dosh-max-ms "${DOSH_BENCH_CACHED_MAX_MS:-25}"; then
docker logs "$container_id" || true
docker exec "$container_id" sh -lc 'cat /tmp/dosh-server.log 2>/dev/null || true' || true
exit 1
fi
if [ "${DOSH_BENCH_INCLUDE_MOSH:-0}" = "1" ]; then if [ "${DOSH_BENCH_INCLUDE_MOSH:-0}" = "1" ]; then
if ! HOME="$workdir/home-mosh" target/release/dosh-bench \ if ! HOME="$workdir/home-mosh" target/release/dosh-bench \
--server bench@127.0.0.1 \ --server bench@127.0.0.1 \
+65 -33
View File
@@ -40,6 +40,10 @@ struct Args {
#[arg(long)] #[arg(long)]
no_cache: bool, no_cache: bool,
#[arg(long)] #[arg(long)]
warm_cache: bool,
#[arg(long)]
skip_ssh_baseline: bool,
#[arg(long)]
include_mosh: bool, include_mosh: bool,
#[arg(long, default_value = "mosh")] #[arg(long, default_value = "mosh")]
mosh: PathBuf, mosh: PathBuf,
@@ -51,6 +55,8 @@ struct Args {
assert_ssh_plus_ms: Option<f64>, assert_ssh_plus_ms: Option<f64>,
#[arg(long)] #[arg(long)]
assert_mosh_minus_ms: Option<f64>, assert_mosh_minus_ms: Option<f64>,
#[arg(long)]
assert_dosh_max_ms: Option<f64>,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
@@ -72,46 +78,25 @@ fn main() -> Result<()> {
} else { } else {
None None
}; };
if args.no_cache && args.warm_cache {
return Err(anyhow!("--warm-cache cannot be used with --no-cache"));
}
let dosh_label = if args.warm_cache {
let _ = time_dosh_attach(&client, &args, control_path)?;
"dosh_cached_attach_ms"
} else {
"dosh_attach_ms"
};
for _ in 0..args.iterations.max(1) { for _ in 0..args.iterations.max(1) {
if !args.local_auth { if !args.local_auth && !args.skip_ssh_baseline {
let mut ssh = Command::new("ssh"); let mut ssh = Command::new("ssh");
add_ssh_options(&mut ssh, &args, control_path); add_ssh_options(&mut ssh, &args, control_path);
ssh.arg(&args.server).arg("true"); ssh.arg(&args.server).arg("true");
ssh_times.push(time_command(&mut ssh)?); ssh_times.push(time_command(&mut ssh)?);
} }
let mut cmd = Command::new(&client); dosh_times.push(time_dosh_attach(&client, &args, control_path)?);
cmd.arg("--attach-only")
.arg("--session")
.arg(&args.session)
.arg("--dosh-port")
.arg(args.dosh_port.to_string());
if let Some(host) = &args.dosh_host {
cmd.arg("--dosh-host").arg(host);
}
if args.local_auth {
cmd.arg("--local-auth").arg(&args.server);
} else {
cmd.arg("--ssh-port")
.arg(args.ssh_port.to_string())
.arg("--ssh-auth-command")
.arg(&args.ssh_auth_command);
if args.no_cache {
cmd.arg("--no-cache");
}
if let Some(key) = &args.ssh_key {
cmd.arg("--ssh-key").arg(key);
}
if let Some(known_hosts) = &args.ssh_known_hosts {
cmd.arg("--ssh-known-hosts").arg(known_hosts);
}
if let Some(control_path) = control_path {
cmd.arg("--ssh-control-path").arg(control_path);
}
cmd.arg(&args.server);
}
dosh_times.push(time_command(&mut cmd)?);
if args.include_mosh { if args.include_mosh {
mosh_times.push(time_mosh_in_pty(&args)?); mosh_times.push(time_mosh_in_pty(&args)?);
@@ -126,7 +111,7 @@ fn main() -> Result<()> {
); );
} }
println!( println!(
"dosh_attach_ms avg={:.2} samples={:?}", "{dosh_label} avg={:.2} samples={:?}",
avg_ms(&dosh_times), avg_ms(&dosh_times),
dosh_times dosh_times
); );
@@ -167,9 +152,56 @@ fn main() -> Result<()> {
} }
println!("gate ok: dosh avg {dosh_avg:.2}ms + {margin:.2}ms <= mosh avg {mosh_avg:.2}ms"); println!("gate ok: dosh avg {dosh_avg:.2}ms + {margin:.2}ms <= mosh avg {mosh_avg:.2}ms");
} }
if let Some(max_ms) = args.assert_dosh_max_ms {
let dosh_avg = avg_ms(&dosh_times);
if dosh_avg > max_ms {
return Err(anyhow!(
"{dosh_label} avg {dosh_avg:.2}ms exceeded max {max_ms:.2}ms"
));
}
println!("gate ok: {dosh_label} avg {dosh_avg:.2}ms <= {max_ms:.2}ms");
}
Ok(()) Ok(())
} }
fn time_dosh_attach(
client: &PathBuf,
args: &Args,
control_path: Option<&PathBuf>,
) -> Result<Duration> {
let mut cmd = Command::new(client);
cmd.arg("--attach-only")
.arg("--session")
.arg(&args.session)
.arg("--dosh-port")
.arg(args.dosh_port.to_string());
if let Some(host) = &args.dosh_host {
cmd.arg("--dosh-host").arg(host);
}
if args.local_auth {
cmd.arg("--local-auth").arg(&args.server);
} else {
cmd.arg("--ssh-port")
.arg(args.ssh_port.to_string())
.arg("--ssh-auth-command")
.arg(&args.ssh_auth_command);
if args.no_cache {
cmd.arg("--no-cache");
}
if let Some(key) = &args.ssh_key {
cmd.arg("--ssh-key").arg(key);
}
if let Some(known_hosts) = &args.ssh_known_hosts {
cmd.arg("--ssh-known-hosts").arg(known_hosts);
}
if let Some(control_path) = control_path {
cmd.arg("--ssh-control-path").arg(control_path);
}
cmd.arg(&args.server);
}
time_command(&mut cmd)
}
fn add_ssh_options(cmd: &mut Command, args: &Args, control_path: Option<&PathBuf>) { fn add_ssh_options(cmd: &mut Command, args: &Args, control_path: Option<&PathBuf>) {
cmd.arg("-p").arg(args.ssh_port.to_string()).arg("-T"); cmd.arg("-p").arg(args.ssh_port.to_string()).arg("-T");
if let Some(key) = &args.ssh_key { if let Some(key) = &args.ssh_key {