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
container, key, DNS, and network path. 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.
container, key, DNS, and network path. It also prints `dosh_cached_attach_ms`, which
is the real Dosh fast path after the first SSH-authenticated bootstrap has issued an
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
`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 |
| --- | --- |
| `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. |
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
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 | Mosh | Dosh now | Public status |
+17 -1
View File
@@ -31,7 +31,7 @@ for _ in 1 2 3 4 5; do
done
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 \
--server bench@127.0.0.1 \
@@ -64,6 +64,22 @@ if ! HOME="$workdir/home-controlmaster" target/release/dosh-bench \
exit 1
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 ! HOME="$workdir/home-mosh" target/release/dosh-bench \
--server bench@127.0.0.1 \
+65 -33
View File
@@ -40,6 +40,10 @@ struct Args {
#[arg(long)]
no_cache: bool,
#[arg(long)]
warm_cache: bool,
#[arg(long)]
skip_ssh_baseline: bool,
#[arg(long)]
include_mosh: bool,
#[arg(long, default_value = "mosh")]
mosh: PathBuf,
@@ -51,6 +55,8 @@ struct Args {
assert_ssh_plus_ms: Option<f64>,
#[arg(long)]
assert_mosh_minus_ms: Option<f64>,
#[arg(long)]
assert_dosh_max_ms: Option<f64>,
}
fn main() -> Result<()> {
@@ -72,46 +78,25 @@ fn main() -> Result<()> {
} else {
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) {
if !args.local_auth {
if !args.local_auth && !args.skip_ssh_baseline {
let mut ssh = Command::new("ssh");
add_ssh_options(&mut ssh, &args, control_path);
ssh.arg(&args.server).arg("true");
ssh_times.push(time_command(&mut ssh)?);
}
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);
}
dosh_times.push(time_command(&mut cmd)?);
dosh_times.push(time_dosh_attach(&client, &args, control_path)?);
if args.include_mosh {
mosh_times.push(time_mosh_in_pty(&args)?);
@@ -126,7 +111,7 @@ fn main() -> Result<()> {
);
}
println!(
"dosh_attach_ms avg={:.2} samples={:?}",
"{dosh_label} avg={:.2} samples={:?}",
avg_ms(&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");
}
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(())
}
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>) {
cmd.arg("-p").arg(args.ssh_port.to_string()).arg("-T");
if let Some(key) = &args.ssh_key {