pcbjam/scripts/bench/vm-build.sh
Istvan Matejcsok df25275843 bench: wasm-opt benchmark harness — Hetzner config sweeps + local QEMU VM
Tooling that produced the perf findings (committed for rerunnability):
- scripts/bench/wasm-opt-bench.sh + o2-config-sweep.sh + sweep.conf:
  replay asyncify/-O2 over a cached fixture under allocator/THP/core
  matrices on the Hetzner runner.
- scripts/bench/setup-vm.sh + cloud-init/ + vm-build.sh: local QEMU
  (HVF) Ubuntu guest with Docker CE to verify Linux builds without
  burning paid runners.
- .gitignore: bench fixtures/results and the VM image stay local.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 07:48:46 +02:00

100 lines
4 KiB
Shell
Executable file

#!/bin/bash
# Run a real docker/build.sh build INSIDE the local QEMU Linux VM, as a cheap
# stand-in for the Hetzner CI runner (Linux + Docker + the same scripts), so CI
# orchestration changes can be verified without burning a Hetzner slot.
# RUNS ON THE macOS HOST. The guest is aarch64 under HVF — near-native speed,
# a correct *functional* proxy for CI but NOT an x86 performance proxy.
#
# Usage:
# ./scripts/bench/vm-build.sh [build.sh args...]
# default args: calculator --build-deps
#
# Examples:
# ./scripts/bench/vm-build.sh # cold calculator build
# KICAD_PIPELINE=1 ./scripts/bench/vm-build.sh calculator,pl_editor # pipeline smoke test
#
# Prereqs (one-time):
# VM_DISK=80G ./scripts/bench/setup-vm.sh prepare # 80G: emsdk image + deps + build tree
# ./scripts/bench/setup-vm.sh run # boot in another terminal, wait for cloud-init
#
# Env: SSH_PORT (2222), KICAD_PIPELINE/KICAD_PIPELINE_JOBS (forwarded to the guest),
# VM_BUILD_JOBS (compile -j inside the guest; default = guest nproc).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
SSH_PORT="${SSH_PORT:-2222}"
GUEST="bench@localhost"
GUEST_DIR="kicad-wasm"
SSH_OPTS=(-p "${SSH_PORT}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)
vssh() { ssh "${SSH_OPTS[@]}" "${GUEST}" "$@"; }
if ! vssh true 2>/dev/null; then
echo "ERROR: VM not reachable on localhost:${SSH_PORT}." >&2
echo "Boot it first: ./scripts/bench/setup-vm.sh run (wait ~2 min for cloud-init)" >&2
exit 1
fi
# Wait until cloud-init finished installing Docker (first boot takes a few min).
echo "Waiting for cloud-init to finish (installs Docker on first boot)..."
vssh "cloud-init status --wait >/dev/null 2>&1 || true"
vssh "docker info >/dev/null 2>&1" || {
echo "ERROR: Docker not usable in the guest. Re-run setup-vm.sh prepare with the" >&2
echo "updated cloud-init (installs docker-ce) and boot a fresh VM." >&2
exit 1
}
# Sync the working tree (incl. submodule content, no .git) into the guest.
# Same exclude set as build.sh's container-sync, plus the VM images themselves.
echo "Syncing repo to ${GUEST}:~/${GUEST_DIR} ..."
rsync -az --delete \
-e "ssh ${SSH_OPTS[*]}" \
--exclude="build-wasm" \
--exclude="output" \
--exclude=".git" \
--exclude="logs" \
--exclude=".idea" \
--exclude="node_modules" \
--exclude="tools/emsdk" \
--exclude="scripts/bench/vm" \
--exclude="tests/test-results" \
--exclude="tests/playwright-report" \
"${PROJECT_ROOT}/" "${GUEST}:${GUEST_DIR}/"
BUILD_ARGS=("$@")
[ ${#BUILD_ARGS[@]} -eq 0 ] && BUILD_ARGS=(calculator --build-deps)
# Append a guest-sized -j unless the caller already passed one.
JOBS_FLAG='-j "${JOBS}"'
[[ " ${BUILD_ARGS[*]} " == *" -j "* ]] && JOBS_FLAG=""
# Guest-side env:
# - COMPOSE_PROJECT_NAME: no .git in the guest tree, so build.sh can't derive a
# branch name — pin the project name explicitly.
# - KICAD_DOCKER_CPUS/MEM: the compose dev-Mac caps (10 CPU / 32G) point at a
# guest that may have fewer cores and definitely has less RAM; size to guest.
# - KICAD_NO_MONITOR/KICAD_LOG_NESTED: no TTY dashboard over ssh, stream output.
REMOTE_CMD=$(cat <<EOF
set -e
cd ${GUEST_DIR}
GUEST_CORES=\$(nproc)
GUEST_MEM_G=\$(awk '/MemTotal/{printf "%d", \$2/1024/1024 - 3}' /proc/meminfo)
export COMPOSE_PROJECT_NAME=kicad-wasm-vm
export KICAD_DOCKER_CPUS=\${GUEST_CORES}
export KICAD_DOCKER_MEM=\${GUEST_MEM_G}G
export KICAD_NO_MONITOR=1 KICAD_LOG_NESTED=1
export KICAD_PIPELINE=${KICAD_PIPELINE:-0} KICAD_PIPELINE_JOBS=${KICAD_PIPELINE_JOBS:-2}
JOBS=${VM_BUILD_JOBS:-\$GUEST_CORES}
echo "=== VM build: \$(uname -m), \${GUEST_CORES} cores, docker mem \${KICAD_DOCKER_MEM}, -j \${JOBS}, args: ${BUILD_ARGS[*]} ==="
time ./docker/build.sh ${BUILD_ARGS[*]} ${JOBS_FLAG}
ls -lh output/
EOF
)
START=$(date +%s)
vssh "${REMOTE_CMD}"
END=$(date +%s)
echo ""
echo "=== VM build wall time: $(( (END - START) / 60 ))m $(( (END - START) % 60 ))s (args: ${BUILD_ARGS[*]}) ==="