Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions .github/workflows/zebra-bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
name: Zebra consensus benchmarks

# Runs the zebra-consensus criterion benches on every zebra-touching commit:
# the transparent script cache bench (benches/script.rs) and the block-path
# UTXO lookup bench (benches/utxo_lookup.rs). Informational, not a required
# check: absolute times on shared runners are noisy, but the ratios the
# benches exist to watch (cache miss vs hit, serial vs overlapped lookups)
# are measured within one run on one box, and an order-of-magnitude
# regression is visible through the noise.
#
# Each bench step is guarded on its bench file existing, so this workflow can
# land before the PRs that add the benches and activates as they merge.

on:
# No `paths:` filter, for the reasons written up at the top of z3-smoke.yml;
# the `changes` job below is the equivalent gate.
pull_request:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
changes:
name: Path gate
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
relevant: ${{ steps.gate.outputs.relevant }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
# Depth 2 diffs the PR merge commit (or the pushed merge) against
# what it landed on.
fetch-depth: 2

- name: Decide whether this change can move the benchmarks
id: gate
env:
EVENT_NAME: ${{ github.event_name }}
run: |
set -euo pipefail

# A manual dispatch is an explicit request; only `push` and
# `pull_request` go through the diff gate.
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
echo "Manual dispatch: benching unconditionally." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi

specs=(
':(glob)zebra/zebra-consensus/**'
':(glob)zebra/zebra-chain/**'
':(glob)zebra/zebra-script/**'
':(glob)zebra/Cargo.toml'
':(glob)zebra/Cargo.lock'
':(glob).github/workflows/zebra-bench.yml'
)

# Fail open: "we could not tell" must never silently mean "no CI".
base="HEAD^1"
if [ "$EVENT_NAME" = "push" ]; then
before=$(jq -r '.before // empty' "$GITHUB_EVENT_PATH" 2>/dev/null || true)
if [ -n "$before" ] && [ "$before" != "0000000000000000000000000000000000000000" ]; then
base="$before"
fi
fi

if ! git diff --name-only "$base" HEAD > "$RUNNER_TEMP/changed" 2>/dev/null; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
echo "::warning::could not diff against the base; benching unconditionally"
echo "Could not diff against the base; benching unconditionally." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
git diff --name-only "$base" HEAD -- "${specs[@]}" > "$RUNNER_TEMP/matched"

total=$(( $(wc -l < "$RUNNER_TEMP/changed") ))
hits=$(( $(wc -l < "$RUNNER_TEMP/matched") ))
if [ "$hits" -gt 0 ]; then relevant=true; else relevant=false; fi
echo "relevant=$relevant" >> "$GITHUB_OUTPUT"

{
echo "### zebra bench path gate"
echo
echo "\`$total\` file(s) changed, \`$hits\` of them reaching the benched crates."
} >> "$GITHUB_STEP_SUMMARY"

bench:
name: zebra-consensus benches
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
# First uncached run builds the zebra workspace's bench profile (~40 min);
# cached runs are dominated by the benches themselves (~10 min).
timeout-minutes: 75
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 1

# rustup resolves zebra's rust-toolchain.toml on first use. This cache
# namespace stays separate from z3-regtest's (different artifact sets),
# and only main saves it: per-PR saves of a multi-GB target tree would
# push the repo past the Actions cache quota and evict the smoke and
# regtest caches. PR runs restore main's entry.
- name: Rust build cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: v1-bench
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: |
zebra -> target

- name: Script cache bench
working-directory: zebra
run: |
set -euo pipefail
if [ ! -f zebra-consensus/benches/script.rs ]; then
echo "benches/script.rs not on this branch yet; skipping" >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
cargo bench -p zebra-consensus --bench script 2>&1 | tee "$RUNNER_TEMP/script.out"
{
echo "### script bench"
echo '```'
grep -B1 -A2 "time:" "$RUNNER_TEMP/script.out" || echo "no timings parsed"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

- name: UTXO lookup bench
working-directory: zebra
run: |
set -euo pipefail
if [ ! -f zebra-consensus/benches/utxo_lookup.rs ]; then
echo "benches/utxo_lookup.rs not on this branch yet; skipping" >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
cargo bench -p zebra-consensus --bench utxo_lookup 2>&1 | tee "$RUNNER_TEMP/utxo.out"
{
echo "### utxo_lookup bench"
echo '```'
grep -B1 -A2 "time:" "$RUNNER_TEMP/utxo.out" || echo "no timings parsed"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"