#!/usr/bin/env bash # Onset against stock git on one machine. git gets its best case: a bare # repository on the same disk, and `git pull --rebase` + push again when a # push is refused (what people and agents do). Onset uses onset-server on # this machine. Same project, same edits. # Usage: tests/bench.sh [path/to/target/release] set -uo pipefail ROOT=$(cd "$(dirname "$0")/.." && pwd) BIN=${1:-$ROOT/target/release} VCS=$BIN/on WORK=$(mktemp -d) export ONSET_SAVER=0 ONSET_HOME=$WORK/home ONSET_NAME=Bench ONSET_EMAIL=bench@example.com export GIT_AUTHOR_NAME=Bench GIT_AUTHOR_EMAIL=bench@example.com GIT_COMMITTER_NAME=Bench GIT_COMMITTER_EMAIL=bench@example.com # Run from inside an agent, each land would also keep the agent's own session. unset CLAUDE_CODE_SESSION_ID CLAUDECODE CLAUDE_CONFIG_DIR ONSET_SESSION_FILE ONSET_SESSIONS VCS_SESSION_FILE VCS_SESSIONS VCS_HOME VCS_TOKEN export CODEX_HOME="$WORK/no-codex" PORT=$((20000 + RANDOM % 20000)) HOST="127.0.0.1:$PORT" SERVER_PID= cleanup() { [ -n "$SERVER_PID" ] && kill "$SERVER_PID" 2>/dev/null rm -rf "$WORK" } trap cleanup EXIT cd "$WORK" g() { git -c init.defaultBranch=main -c commit.gpgsign=false -c advice.detachedHead=false "$@"; } now() { python3 -c 'import time; print(time.time())'; } since() { python3 -c "import time; print(f'{time.time() - $1:.2f}')"; } best() { # fastest of 5 runs, in ms python3 - "$@" <<'EOF' import subprocess, sys, time best = 1e9 for _ in range(5): t = time.time(); subprocess.run(sys.argv[1:], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL); best = min(best, time.time() - t) print(f"{best * 1000:.0f}") EOF } files() { # folder, count python3 - "$1" "$2" <<'EOF' import os, sys root, n = sys.argv[1], int(sys.argv[2]) for i in range(n): d = os.path.join(root, "src", f"m{i // 20:04d}") os.makedirs(d, exist_ok=True) with open(os.path.join(d, f"f{i:05d}.ts"), "w") as f: f.write(f"export const v{i} = {i};\n" * 30) EOF } history() { # git folder, commits: each changes three lines of hot.ts (500 lines) and all of count.ts python3 - "$2" <<'EOF' | (cd "$1" && g fast-import --quiet) import random, sys n = int(sys.argv[1]) random.seed(1) lines = [f"export const line{i} = {i};" for i in range(500)] out = sys.stdout.buffer def data(b): out.write(b"data %d\n" % len(b) + b + b"\n") for c in range(n): for _ in range(3): i = random.randrange(len(lines)) lines[i] = f"export const line{i} = {c}; // commit {c}" out.write(b"commit refs/heads/main\ncommitter Bench %d +0000\n" % (1700000000 + c * 60)) data(f"Commit {c}".encode()) out.write(b"M 100644 inline hot.ts\n") data(("\n".join(lines) + "\n").encode()) out.write(b"M 100644 inline count.ts\n") data(f"export const n = {c};\n".encode()) EOF } row() { printf '%-52s %12s %12s\n' "$1" "$2" "$3"; } "$BIN/onset-server" user add bench --name Bench --email bench@example.com --data "$WORK/server" > token "$BIN/onset-server" serve --data "$WORK/server" --listen "$HOST" --threads 32 2>/dev/null & SERVER_PID=$! for _ in $(seq 50); do curl -s "http://$HOST/.well-known/onset.json" >/dev/null && break; sleep 0.1; done "$VCS" login "$HOST" --token "$(cat token)" >/dev/null echo "Onset $("$VCS" --version | cut -d' ' -f2) vs $(git --version) ($(nproc) cores, $(uname -s))" echo row "" "git" "Onset" # The same 20,000-file project in both. mkdir gseed && files gseed 20000 && (cd gseed && g init -q && g add -A && g commit -qm "20k files") g clone -q --bare gseed big.git "$VCS" init vseed >/dev/null && files vseed 20000 && "$VCS" -C vseed land -m "20k files" >/dev/null "$VCS" -C vseed publish "$HOST/bench/big" >/dev/null g clone -q big.git gbig "$VCS" get "$HOST/bench/big" vbig >/dev/null sleep 1.5 row "status, 20,000 files, nothing changed (ms)" "$(best git -C gbig status)" "$(best "$VCS" -C vbig status)" printf 'edit\n' >> gbig/src/m0500/f10000.ts printf 'edit\n' >> vbig/src/m0500/f10000.ts row "status, 20,000 files, one file changed (ms)" "$(best git -C gbig status)" "$(best "$VCS" -C vbig status)" git -C gbig checkout -q -- . && "$VCS" -C vbig restore src/m0500/f10000.ts >/dev/null t=$(now); g clone -q --no-local "file://$WORK/big.git" gfresh; tg=$(since "$t") t=$(now); ONSET_HOME=$WORK/fresh "$VCS" login "$HOST" --token "$(cat token)" >/dev/null; ONSET_HOME=$WORK/fresh "$VCS" get "$HOST/bench/big" vfresh >/dev/null; tv=$(since "$t") row "first copy on a new machine, 20,000 files (s)" "$tg" "$tv" t=$(now); git -C gbig worktree add -q ../gwt >/dev/null 2>&1; tg=$(since "$t") t=$(now); "$VCS" get "$HOST/bench/big" vbig2 >/dev/null; tv=$(since "$t") row "second working copy, same machine (s)" "$tg" "$tv" # One small change, start to finish, ten times: commit + push vs land. gl=() vl=() for i in $(seq 1 10); do printf 'change %s\n' "$i" >> gbig/README.md t=$(now); (cd gbig && g add -A && g commit -qm "Change $i" && g push -q origin HEAD:main); gl+=("$(since "$t")") printf 'change %s\n' "$i" >> vbig/README.md t=$(now); "$VCS" -C vbig land -m "Change $i" >/dev/null; vl+=("$(since "$t")") done med() { printf '%s\n' "$@" | sort -n | sed -n "$(( ($# + 1) / 2 ))p"; } row "one change: commit + push vs land, median (s)" "$(med "${gl[@]}")" "$(med "${vl[@]}")" # A long history: the same 5,000 commits in both (Onset's imported from git's, # then published and fetched into a fresh workspace, as a teammate's would be). mkdir glong && (cd glong && g init -q) && history glong 5000 && git -C glong reset -q --hard main "$VCS" import glong vlocal >/dev/null && "$VCS" -C vlocal publish "$HOST/bench/long" >/dev/null "$VCS" get "$HOST/bench/long" vlong >/dev/null "$VCS" -C vlong log -n 1 >/dev/null row "log, newest 100 of 5,000 versions (ms)" "$(best git -C glong log -n 100)" "$(best "$VCS" -C vlong log -n 100)" row "log of one file changed in all 5,000 (ms)" "$(best git -C glong log -n 100 -- hot.ts)" "$(best "$VCS" -C vlong log -n 100 hot.ts)" row "blame, 500 lines changed over 5,000 versions (ms)" "$(best git -C glong blame hot.ts)" "$(best "$VCS" -C vlong blame hot.ts)" # Many agents, one branch, at once. Each makes one change to its own file. race() { # agents local n=$1 rm -rf race && mkdir race && cd race mkdir seed && files seed 1500 && (cd seed && g init -q && g add -A && g commit -qm base) g clone -q --bare seed server.git for i in $(seq 1 "$n"); do g clone -q server.git "g$i" && printf '%s\n' "$i" > "g$i/agent-$i.txt" && (cd "g$i" && g add -A && g commit -qm "Agent $i"); done "$VCS" init vseed >/dev/null && files vseed 1500 && "$VCS" -C vseed land -m base >/dev/null && "$VCS" -C vseed publish "$HOST/bench/race$n" >/dev/null for i in $(seq 1 "$n"); do "$VCS" get "$HOST/bench/race$n" "v$i" >/dev/null && printf '%s\n' "$i" > "v$i/agent-$i.txt"; done sync local t pids=() t=$(now) for i in $(seq 1 "$n"); do (cd "g$i"; tries=0; until g push -q origin HEAD:main 2>/dev/null; do tries=$((tries + 1)); g pull -q --rebase origin main 2>/dev/null; done; echo "$tries" > "../tries$i") & pids+=($!) done wait "${pids[@]}" local tg refused tg=$(since "$t") refused=$(cat tries* | python3 -c "import sys; print(sum(int(l) for l in sys.stdin))") pids=() t=$(now) for i in $(seq 1 "$n"); do ("$VCS" -C "v$i" land -m "Agent $i" >/dev/null 2>&1; echo $? > "vcode$i") & pids+=($!); done wait "${pids[@]}" local tv landed tv=$(since "$t") landed=$(grep -l '^0$' vcode* | wc -l | tr -d ' ') row "$n agents land one change each on one branch (s)" "$tg" "$tv" row " refused pushes / changes that didn't land" "$refused" "$((n - landed))" cd "$WORK" } race 8 race 32 race 64