From fd1a5d741ff237dfc682246f8d1e464c7ab07172 Mon Sep 17 00:00:00 2001 From: Matt Schultz Date: Mon, 3 Aug 2026 01:30:18 -0500 Subject: [PATCH] Add manual benchmark workflow with benchstat comparison Add a workflow_dispatch-triggered Benchmarks workflow so benchmarks can be run on GitHub-hosted runners on demand (Actions tab -> Benchmarks -> Run workflow). - Runs on both amd64 (ubuntu-latest) and arm64 (ubuntu-24.04-arm) hosted runners, since the two architectures differ in general-purpose register count and these register-bound ARX ciphers are sensitive to it. - Installs benchstat (golang.org/x/perf) and uses -count=6 so results are reported as median +/- variance rather than raw repeated lines. - Optional compare_ref input: when set to a branch/tag/SHA, the workflow also benchmarks that ref and prints a benchstat A/B table (delta + p-value) versus the current ref. Branch names are resolved via origin/ (after fetch-depth:0), falling back to the ref verbatim for tags and SHAs. - Results are published to each job summary, labeled with runner/arch/Go version. --- .github/workflows/benchmarks.yml | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/benchmarks.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 0000000..db7f476 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,74 @@ +name: Benchmarks + +# Run on demand only: Actions tab -> Benchmarks -> Run workflow. +on: + workflow_dispatch: + inputs: + compare_ref: + description: 'Optional git ref (branch, tag, or SHA) to compare the current ref against with benchstat. Leave blank to just report current numbers.' + required: false + default: '' + +jobs: + benchmark: + name: Benchmark (${{ matrix.runner }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + # Run on both amd64 and arm64 hosted runners; the two architectures + # have different general-purpose register counts, which materially + # affects these register-bound ARX ciphers. + runner: ['ubuntu-latest', 'ubuntu-24.04-arm'] + steps: + - name: Check out code + uses: actions/checkout@v7 + with: + # Full history so an arbitrary compare_ref can be checked out. + fetch-depth: 0 + + - name: Set up go + uses: actions/setup-go@v7 + with: + go-version: '1.26' + cache: false + + - name: Install benchstat + run: | + go install golang.org/x/perf/cmd/benchstat@latest + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + + - name: Run benchmarks + env: + COMPARE_REF: ${{ inputs.compare_ref }} + run: | + bench=(-run='^$' -bench=. -benchmem -count=6 ./...) + + echo "==> benchmarking current ref ($(git rev-parse --short HEAD))" + go test "${bench[@]}" | tee "$RUNNER_TEMP/head.txt" + + { + echo "### Benchmarks — ${{ matrix.runner }} ($(go env GOARCH), $(go version | awk '{print $3}'))" + echo + if [ -n "$COMPARE_REF" ]; then + # After fetch-depth:0, branches exist as remote-tracking refs, so + # prefer origin/; fall back to the ref verbatim for tags/SHAs. + if git rev-parse --verify --quiet "origin/$COMPARE_REF^{commit}" >/dev/null; then + base_ref="origin/$COMPARE_REF" + else + base_ref="$COMPARE_REF" + fi + echo "==> benchmarking compare ref ($COMPARE_REF -> $base_ref)" >&2 + git checkout --quiet --detach "$base_ref" + go test "${bench[@]}" | tee "$RUNNER_TEMP/base.txt" >&2 + echo "Comparison: \`$COMPARE_REF\` (base) → current ref" + echo + echo '```' + benchstat "$COMPARE_REF=$RUNNER_TEMP/base.txt" "current=$RUNNER_TEMP/head.txt" + echo '```' + else + echo '```' + benchstat "$RUNNER_TEMP/head.txt" + echo '```' + fi + } | tee -a "$GITHUB_STEP_SUMMARY"