Skip to content

perf(ci): build gap-suite's fast-mode archives once, share across shards - #8686

Closed
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:perf/gap-suite-shared-build
Closed

perf(ci): build gap-suite's fast-mode archives once, share across shards#8686
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:perf/gap-suite-shared-build

Conversation

@jdalton

@jdalton jdalton commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Cuts roughly 50 minutes of pure redundant compute from every PR run by building gap-suite's fast-mode release archives once instead of independently rebuilding them in each of the 6 parallel shards.

What was happening and the measured cost

Every gap-suite shard runs cargo build --release -p perry -p perry-runtime -p perry-stdlib -p perry-runtime-static -p perry-stdlib-static before running its own slice of the suite. I pulled the actual per-step timing from a real PR run: that build step alone took 10.0 minutes, and it produces byte-identical output in every shard, since all 6 shards build from the same commit with the same flags.

Because the shards run in parallel, this did not directly cost PR wall-clock time. It did cost roughly 50 minutes of runner compute per PR run (5 unnecessary repeats of a 10-minute build), and on a capacity-limited runner pool that compute pressure can itself delay when other queued jobs get a slot.

Changes

Adds a gap-suite-build job that runs only when the plan selects fast mode (PR and sweep tiers; full mode's shards genuinely need their own per-test auto-optimize rebuild through run_gap_tests.sh, which this does not touch). It builds once, then uploads the three files the fast-mode harness actually links against: the perry binary, libperry_runtime.a, and libperry_stdlib.a. Every gap-suite shard now downloads that artifact instead of rebuilding, restoring the binary's executable bit defensively after the download. gap-suite's own if: condition uses always() together with an explicit check on gap-suite-build's result, so a build that is correctly SKIPPED (full mode) does not cascade into skipping the shards, while a genuine build FAILURE still stops them from starting rather than running six shards doomed to fail identically.

While in this section of the file, I also inlined dtolnay/rust-toolchain and SHA-pinned actions/checkout, actions/setup-node, and Swatinem/rust-cache in the existing gap-suite job, for local consistency with the new job right above it. The broader sweep across the rest of the workflow tree is a separate PR, #8684, since that change is unrelated to this one in scope.

Related issue

n/a

Test plan

I confirmed the per-step timing of the redundant build against a real PR run (https://github.com/PerryTS/perry/actions/runs/32674242904, shard 5's job), which showed the 10.0-minute build step. I traced the fan-in gate job's own gating script and confirmed it only inspects the result of jobs explicitly listed in its own needs: array, which does not include gap-suite-build — so the new job is fully transparent to branch protection, and only gap-suite's own result affects it. I validated the edited workflow file as YAML and ran actionlint against it before and after this change; the only differences are line-number shifts in three pre-existing shellcheck findings elsewhere in the file, with no new findings introduced. I have not been able to run this workflow for real, since that requires a merge to actually exercise the new job graph on this repository's runners.

Checklist

I have not bumped the workspace version or edited CLAUDE.md or CHANGELOG.md. My commit follows the perf: prefix convention used in the log. I have read CONTRIBUTING.md and agree to the Code of Conduct.

Summary by CodeRabbit

  • CI Improvements
    • Faster gap-suite runs by building shared release artifacts once and reusing them across test shards.
    • Full-mode runs continue using their existing optimization workflow.
    • Improved workflow reliability with pinned tool versions and executable downloaded binaries.

Each of gap-suite's 6 PR-tier shards independently ran the exact same 'cargo build --release -p perry -p perry-runtime -p perry-stdlib -p perry-runtime-static -p perry-stdlib-static' - measured at 10.0 minutes per shard against a warm sccache. That is ~50 minutes of pure redundant compute per PR run for identical output.

Adds a gap-suite-build job that runs this build once (only in fast mode; full mode's shards genuinely need their own per-test auto-optimize rebuild, which run_gap_tests.sh already handles and this does not touch), uploads the three files the fast-mode harness actually links against (the perry binary, libperry_runtime.a, libperry_stdlib.a), and has every gap-suite shard download them instead of rebuilding. gap-suite's own if: uses always() plus an explicit check on gap-suite-build's result, so a SKIPPED build (full mode, where the job never runs) does not cascade into skipping the shards, while a genuine build FAILURE correctly stops them from starting.

Also inlines dtolnay/rust-toolchain and SHA-pins actions/checkout, actions/setup-node, and Swatinem/rust-cache in the existing gap-suite job for local consistency with the new gap-suite-build job right above it - the broader sweep across the rest of the workflow tree is PerryTS#8684, a separate PR.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4abfb5ac-6f8a-4bd0-bbf8-0796bc86e403

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad718a and 62ed874.

📒 Files selected for processing (1)
  • .github/workflows/test.yml

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The gap suite now builds fast-mode compiler and runtime artifacts once, uploads them, and reuses them across shards. Full mode retains its auto-optimize path. Related workflow actions and toolchains use pinned commit references.

Changes

Gap suite artifact reuse

Layer / File(s) Summary
Shared fast-mode artifact build
.github/workflows/test.yml
The workflow adds a conditional gap-suite-build job that builds and uploads compiler and runtime archives. The gap-suite job waits for a successful build or a skipped build in full mode.
Shard setup and pinned actions
.github/workflows/test.yml
Fast-mode shards download the shared artifact and restore compiler executable permissions. Full-mode shards retain auto-optimize behavior. Gap-suite actions and toolchain setup use immutable commit references.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 62ed8

This localized CI workflow change has no actionable merge-blocking risk remaining; it is merge-ready after normal checks and review.

Suggested reviewers: proggeramlug, thehypnoo

Sequence Diagram(s)

sequenceDiagram
  participant gap_suite_build
  participant artifact_storage
  participant gap_suite
  gap_suite_build->>artifact_storage: Upload compiler and runtime archives
  gap_suite->>artifact_storage: Download shared archives in fast mode
  artifact_storage-->>gap_suite: Return shared artifacts
  gap_suite->>gap_suite: Restore compiler executable permissions
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main CI optimization: building fast-mode archives once and sharing them across shards.
Description check ✅ Passed The description covers the required summary, changes, related issue, test plan, and checklist with clear implementation and validation details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (1 skipped: 1 unsupported.)
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

proggeramlug added a commit that referenced this pull request Aug 24, 2026
…CI hardening (#8696)

Lands #8687, #8686 and #8684.

#8687 (closes #8679) replaces the post-RS4GC instruction-budget hard
refusal with a typed spill-retry: an already-lowered LlFunction switches
from native statepoint roots to a complete precise shadow frame and the
unit is rebuilt at the originally requested optimization level. This is
the durable handling for the estimator misses #8678 could only make more
accurate.

Retry termination is guaranteed rather than argued.
`request_shadow_frame_spill()` latches on `force_shadow_frame` and
returns false if already set; `apply_budget_spill_retry` records only the
functions where it returned true, and any violation not recorded becomes
a hard error naming it. A function therefore cannot be retried twice.

#8686 builds the gap suite's fast-mode archives once in a dedicated
`gap-suite-build` job and shares them across the six shards. The
skipped-vs-failed distinction is handled explicitly: `always()` keeps a
SKIPPED build (full mode, where the job never runs) from cascading into
skipped shards, while the guard still requires `success` or `skipped`, so
a genuine build FAILURE stops the shards. The shard also verifies the
downloaded binary is runnable and exports PERRY_BIN / PERRY_RUNTIME_DIR.

#8684 inlines dtolnay/rust-toolchain and SHA-pins every other
third-party action. Verified no job and no gate is dropped: the single
removed step is the fast-mode archive build, which moved into
gap-suite-build.

No version bump.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
@proggeramlug

Copy link
Copy Markdown
Contributor

Landed on main via #8696 (squash fdbddebe6).

The thing I checked hardest was the skipped-vs-failed distinction, since a skipped job satisfies a dependency and that has produced gates here that couldn't fail. Your guard gets it right: always() stops a SKIPPED build (full mode) from cascading into skipped shards, while result == 'success' || result == 'skipped' still lets a genuine build FAILURE stop them. The mode-gated download, the runnable-binary check, and the explicit PERRY_BIN/PERRY_RUNTIME_DIR export all matter too — without that last one the wrapper exits 0 having run nothing.

Validated on the merged result: all 30 lint checkers, codegen 1202/0, runtime 2655/0, perry-bin 1031/0. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants