Files

95 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Reproducible measurements behind COMPARISON.md and the charts. Writes JSON (default docs/charts/bench.json).
# Tools that are not installed or not usable are skipped and recorded as null, never guessed.
# Usage: scripts/bench.sh [output.json] Environment: BENCH_RUNS (default 10)
set -u
cd "$(dirname "$0")/.."
OUT=${1:-docs/charts/bench.json}
RUNS=${BENCH_RUNS:-10}
DOOS=$(readlink -f build/doos)
[ -x "$DOOS" ] || { echo "bench: build first (make build)" >&2; exit 1; }
export DOOS_HOME=$(mktemp -d)
trap '"$DOOS" delete --yes ha >/dev/null 2>&1; rm -rf "$DOOS_HOME"' EXIT
have() { command -v "$1" >/dev/null 2>&1; }
now() { date +%s%N; }
# avg_ms LABEL CMD... -> prints average ms per run, or "null" when the command fails
avg_ms() {
timeout 180 "$@" >/dev/null 2>&1 || { echo null; return; } # warm-up, may pull an image
local s e i; s=$(now)
for i in $(seq "$RUNS"); do timeout 60 "$@" >/dev/null 2>&1 || { echo null; return; }; done
e=$(now); awk -v s="$s" -v e="$e" -v n="$RUNS" 'BEGIN{printf "%.1f", (e-s)/1e6/n}'
}
note() { echo "bench: $*" >&2; }
# ---- setup doos containers (host mode needs nothing, image mode downloads Alpine once)
"$DOOS" image add alpine >/dev/null 2>&1 || note "alpine image unavailable, image-mode rows will be null"
"$DOOS" get_or_create --net none hn >/dev/null 2>&1
"$DOOS" get_or_create hs >/dev/null 2>&1
"$DOOS" get_or_create --image alpine --net none an >/dev/null 2>&1
"$DOOS" get_or_create --image alpine as >/dev/null 2>&1
"$DOOS" get_or_create --image alpine --net pasta ap >/dev/null 2>&1
BW_BASE="--unshare-user --unshare-pid --unshare-ipc --unshare-uts --die-with-parent --ro-bind /usr /usr --symlink usr/bin /bin --symlink usr/lib /lib --symlink usr/lib64 /lib64 --proc /proc --dev /dev"
rows=()
add() { rows+=("{\"id\":\"$1\",\"label\":\"$2\",\"group\":\"$3\",\"kind\":\"$4\",\"ms\":$5}"); }
echo "bench: startup, $RUNS runs each" >&2
if have bwrap; then
add bwrap_none "bubblewrap (host /usr)" none other "$(avg_ms bwrap --unshare-all $BW_BASE true)"
add bwrap_net "bubblewrap (host /usr)" default other "$(avg_ms bwrap $BW_BASE true)"
fi
add doos_host_none "doos host mode" none doos "$(avg_ms "$DOOS" attach hn -- true)"
add doos_alpine_none "doos Alpine image" none doos "$(avg_ms "$DOOS" attach an -- true)"
add doos_host_slirp "doos host mode" default doos "$(avg_ms "$DOOS" attach hs -- true)"
add doos_alpine_slirp "doos Alpine image" default doos "$(avg_ms "$DOOS" attach as -- true)"
add doos_alpine_pasta "doos Alpine, pasta" default doos "$(avg_ms "$DOOS" attach ap -- true)"
if have podman; then
add podman_none "Podman Alpine" none other "$(avg_ms podman run --rm --network none alpine true)"
add podman_net "Podman Alpine" default other "$(avg_ms podman run --rm alpine true)"
fi
if have docker; then
add docker_none "Docker Alpine (rootful)" none other "$(avg_ms docker run --rm --network none alpine true)"
add docker_net "Docker Alpine (rootful)" default other "$(avg_ms docker run --rm alpine true)"
fi
# ---- binary sizes
size_of() { local p; p=$(command -v "$1" 2>/dev/null) && stat -L -c %s "$p" || echo null; }
sizes=()
for t in bwrap crun doos docker podman; do
if [ "$t" = doos ]; then b=$(stat -c %s "$DOOS"); else b=$(size_of "$t"); fi
sizes+=("{\"id\":\"$t\",\"bytes\":$b}")
done
# ---- cpu limit: 4 busy loops for 4 seconds, CPU seconds consumed (user+sys)
cpu_run() {
/usr/bin/time -f "%U %S" "$DOOS" attach "$1" -- sh -c 'for i in 1 2 3 4; do (timeout 4 sh -c "while :; do :; done" &); done; sleep 4.2' 2>&1 >/dev/null | tail -1 | awk '{printf "%.2f", $1+$2}'
}
echo "bench: cpu limit" >&2
"$DOOS" get_or_create hc >/dev/null 2>&1; "$DOOS" get_or_create --cpus 0.5 hc5 >/dev/null 2>&1
cpu_free=$(cpu_run hc); cpu_half=$(cpu_run hc5)
"$DOOS" delete --yes hc >/dev/null 2>&1; "$DOOS" delete --yes hc5 >/dev/null 2>&1
ver() { "$@" 2>/dev/null | head -1 | tr -d '"\\' ; }
json_join() { local IFS=,; echo "$*"; }
cat > "$OUT" <<JSON
{
"meta": {
"date": "$(date +%F)",
"kernel": "$(uname -r)",
"cpus": $(nproc),
"runs": $RUNS,
"doos": "$(cat VERSION)",
"podman": "$(have podman && ver podman --version || echo none)",
"docker": "$(have docker && ver docker --version || echo none)",
"bubblewrap": "$(have bwrap && ver bwrap --version || echo none)",
"src_lines": $(cat src/*.cpp src/*.hpp | wc -l),
"shared_libs": $(ldd "$DOOS" | wc -l)
},
"startup": [$(json_join "${rows[@]}")],
"sizes": [$(json_join "${sizes[@]}")],
"cpu": {"wall_seconds": 4.2, "busy_loops": 4, "unlimited": ${cpu_free:-null}, "half_cpu": ${cpu_half:-null}}
}
JSON
echo "bench: wrote $OUT" >&2