95 lines
4.6 KiB
Bash
Executable File
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
|