9.8 KiB
Dosh Benchmarks
This document explains how to run the Dosh benchmarks, what each metric means, and how to read the results honestly. Dosh's headline claim is insane speed on repeat attach/reconnect — these benchmarks exist to make that claim measurable and reproducible.
Read first:
docs/PUBLIC_READINESS.md. The defensible public claim is fast attach/reconnect, not full Mosh feature parity, and not cold startup. Citedosh_cached_attach_msfor the core speed claim; cite cold metrics only to show the fallback path stays competitive with ordinary SSH.
TL;DR
make bench-local # safe, self-contained matrix on a throwaway server
make bench-local-json # same, machine-readable JSON with raw samples
make bench-docker-ssh # containerized SSH-vs-Dosh gate used by CI
make bench-docker-mosh # same, with Mosh installed for a three-way comparison
make bench-local never touches a running production server: it builds release
binaries, starts its own dosh-server bound to 127.0.0.1 on a random free UDP
port inside a temporary HOME, runs the matrix, prints raw samples plus summary
statistics, and tears everything down on exit. It will refuse to bind UDP port
50000 (the default production port).
The benchmark binary: dosh-bench
dosh-bench spawns the real dosh-client once per iteration and times the whole
process from launch to terminal-ready-and-detached. That wall-clock cost is the
apples-to-apples comparison against ssh host true: it is the startup price each
tool pays before any useful remote work begins.
Every run prints, per metric, a summary line (count, min, median, p95, mean, max
in milliseconds) and a raw-samples line. Pass --json for one machine-readable
object per run (with the full samples_ms array) so published numbers can always
include raw data, as required by docs/PUBLIC_READINESS.md.
Path matrix
dosh-bench can benchmark these Dosh attach paths. Without an explicit path flag
it keeps its legacy single-path behavior (driven by --local-auth / --warm-cache
/ --no-cache) so existing CI scripts keep working.
| Flag | Metric | What it measures |
|---|---|---|
--cold-native |
dosh_cold_native_ms |
Native cold auth: full --auth native --no-cache handshake to first frame and detach. The cold fallback when no cache exists. |
--cached-ticket |
dosh_cached_attach_ms |
Cached attach-ticket fast path. Warms the cache once, then measures repeat attaches using cached UDP credentials/tickets. This is the core speed claim. |
--resume |
dosh_resume_ms |
UDP resume of a session whose endpoint changes. See the caveat below — only meaningful when a live session is kept open out-of-band. |
--local-auth |
dosh_local_attach_ms |
Self-contained local bootstrap; no SSH, no native handshake. Useful as a lower-bound sanity check on loopback. |
| (default, no flag) | dosh_attach_ms |
Cold SSH bootstrap plus UDP attach (legacy single-path mode). |
Existing baseline/comparison metrics are unchanged:
| Metric | Meaning |
|---|---|
ssh_true_ms |
ssh host true with the same key/options. |
mosh_start_true_ms |
mosh host -- true bootstrap, run true, exit (timed inside a PTY). |
Assertions / gates
| Flag | Gate |
|---|---|
--assert-ssh-plus-ms N |
The primary Dosh metric must be ≤ ssh_true_ms mean + N ms. |
--assert-mosh-minus-ms N |
The primary Dosh metric must be at least N ms faster than mosh_start_true_ms. |
--assert-dosh-max-ms N |
The primary Dosh metric mean must be ≤ N ms. |
The "primary Dosh metric" for assertions is, in priority order:
dosh_cached_attach_ms → dosh_resume_ms → dosh_cold_native_ms →
dosh_attach_ms → dosh_local_attach_ms.
What each benchmark does
make bench-local (scripts/bench-local.sh)
Self-contained, no SSH, no Docker. It:
- Builds release binaries (
DOSH_BENCH_PROFILE=debugfor the debug profile). - Picks a free UDP port (never
50000) and a tempHOME. - Generates a throwaway client identity (
ssh-keygen), authorizes it on the server, and writes a throwaway server + client config (native auth, trust-on-first-use, localhost UDP). - Starts
dosh-server servebound to127.0.0.1on that port. - Runs
dosh-bench --cold-native --cached-ticket, then--local-auth --no-cache. - Sanity-checks that native cold auth actually trusted the host (so a silent config-parse fallback can't make the benchmark measure the wrong path).
- Kills the server and removes the temp
HOMEon exit.
Tunables: scripts/bench-local.sh [ITERATIONS] (default 20), DOSH_BENCH_JSON=1,
DOSH_BENCH_PROFILE=release|debug.
make bench-docker-ssh / make bench-docker-mosh (scripts/ci-docker-ssh-bench.sh)
Builds one Ubuntu image with OpenSSH, dosh-server, dosh-auth (and Mosh for the
-mosh target), then runs ssh_true_ms, cold + ControlMaster SSH-bootstrap
dosh_attach_ms, one-time dosh trust followed by native-cold
dosh_cold_native_ms, dosh_cached_attach_ms, and optionally
mosh_start_true_ms against the same container, key, and loopback network path.
This is the gate CI enforces; it asserts cold SSH-bootstrap Dosh stays within
500 ms of SSH, native-cold Dosh beats SSH mean by default, and cached attach stays
under a small budget. See README.md "Develop" for the exact invocations.
Methodology and caveats
- Same machine, same network path.
bench-localruns everything on loopback, so it isolates Dosh's own process/handshake/render overhead and removes network RTT from the comparison. Real-world cached attach is approximately network RTT plus the local overhead these numbers measure. Publish the machine, OS, CPU, network path, and sample count alongside any numbers. - Wall-clock of the whole client process. Each sample includes process spawn,
attach, first-frame render, and detach. That is deliberately the same thing
ssh host truepays, but it means a few ms is fixed process-startup cost, not protocol cost. - Do not cite cold metrics as the headline.
dosh_cold_native_msanddosh_attach_msstill pay first-authentication cost. They exist to show the fallback path stays competitive with ordinary SSH. Citedosh_cached_attach_msfor repeat attach/reconnect, perdocs/PUBLIC_READINESS.md. - UDP resume is the roaming path, not cold reconnect. Resume reconnects a
client that is still attached when its network endpoint changes (sleep/Wi-Fi
switch). The
--attach-onlybenchmark model detaches after every iteration, which removes the client on the server, so a fresh-process resume has nothing live to resume and is not meaningful inbench-local. The cold fresh-process reconnect fast path is the attach ticket (dosh_cached_attach_ms). Roaming resume correctness is covered by the integration testresume_updates_udp_endpoint_for_roamingintests/integration_smoke.rs.dosh-bench --resumeremains available for scenarios that keep a live session open out-of-band (for example remote soak tests). - These are not identical workloads. SSH/Mosh/Dosh do different work at startup. The comparison is still useful because it measures the startup tax each tool charges before useful remote work begins.
Sample results (loopback, self-contained)
Captured with make bench-local (scripts/bench-local.sh 20), all times in
milliseconds, 20 samples per metric.
- Machine: Intel Core i5-9500 @ 3.00 GHz, 6 cores
- OS: Ubuntu 24.04.4 LTS, Linux 6.8.0-124-generic, x86_64
- Toolchain: rustc 1.96.0, release profile
- Network path: loopback (
127.0.0.1), throwaway server on a free UDP port - Workload:
dosh-client --attach-onlyto first frame and detach
| Metric | n | min | median | p95 | mean | max |
|---|---|---|---|---|---|---|
dosh_cold_native_ms |
20 | 8.32 | 8.77 | 9.34 | 8.82 | 9.52 |
dosh_cached_attach_ms |
20 | 2.83 | 3.23 | 3.75 | 3.24 | 3.77 |
dosh_local_attach_ms |
20 | 2.84 | 3.21 | 3.96 | 3.28 | 3.98 |
Raw samples (ms):
dosh_cold_native_ms:
8.82, 8.60, 8.69, 8.71, 9.33, 8.82, 8.90, 8.64, 8.39, 8.75, 8.79, 8.70, 8.64,
9.52, 8.72, 8.32, 9.06, 8.93, 8.96, 9.04
dosh_cached_attach_ms:
3.04, 3.06, 2.98, 2.91, 2.96, 2.95, 3.05, 3.66, 3.37, 2.88, 2.83, 3.16, 3.49,
3.30, 3.77, 3.32, 3.49, 3.75, 3.56, 3.33
dosh_local_attach_ms:
3.08, 3.96, 3.36, 3.30, 2.88, 2.99, 3.96, 3.98, 3.25, 2.84, 3.29, 3.59, 3.65,
3.47, 3.13, 2.92, 2.87, 2.91, 2.98, 3.18
Reading these numbers: cold native auth is ~9 ms because it pays the native
handshake; cached attach is ~3 ms because it reuses cached credentials and skips
the handshake entirely. On loopback there is no network RTT, so this is the local
overhead floor. Over a real link, expect cached attach ≈ this floor + one network
round trip — which is exactly the "near network RTT" target in the spec. There is
no SSH/Mosh baseline in this loopback table because those paths need an SSH server;
use make bench-docker-ssh / make bench-docker-mosh for the head-to-head.
Sample results (Docker SSH/Mosh comparison)
Captured with make bench-docker-mosh, all times in milliseconds. This is the
same-container comparison gate: one generated key, one OpenSSH server, one
dosh-server, loopback-published TCP/UDP ports.
| Metric | n | min | median | p95 | mean | max |
|---|---|---|---|---|---|---|
ssh_true_ms (cold SSH baseline for native-cold gate) |
5 | 214.53 | 216.84 | 220.55 | 217.60 | 220.91 |
dosh_cold_native_ms |
5 | 8.62 | 8.96 | 9.78 | 9.15 | 9.88 |
dosh_cached_attach_ms |
10 | 8.41 | 8.83 | 9.52 | 8.90 | 9.53 |
mosh_start_true_ms |
3 | 528.03 | 530.87 | 539.23 | 533.02 | 540.16 |
Raw samples (ms):
ssh_true_ms (native-cold gate):
216.84, 216.61, 214.53, 220.91, 219.09
dosh_cold_native_ms:
9.39, 8.88, 8.96, 8.62, 9.88
dosh_cached_attach_ms:
8.44, 8.41, 8.80, 8.48, 9.53, 9.51, 8.85, 8.69, 9.33, 8.96
mosh_start_true_ms:
540.16, 528.03, 530.87