Skip to content

CI: zig and zig-windows spend most of their time rerunning tests that can't have changed #2242

Closed

Description

@ashhart

Problem

zig and zig-windows take about 18 and 26 minutes on pull requests (p50, 63 runs on 5 Nov), and pull request jobs wait 5 to 10 minutes for a runner at the median and 34 to 39 at p90. Those two jobs are 44 of the 61 job-hours in that window, so they also set the wait for everything else, and landing waits on all of it.

Where the time goes

  • Builds start from nothing. mlugg/setup-zig empties its cache past 2 GiB and saves the empty folder, and both jobs have outgrown that.
  • Every test reruns. Running the tests takes 10 to 13 minutes even with nothing to compile, so a warm cache alone gains little on Linux. A PR that changes only docs pays the full cost.
  • A few binaries dominate. In one cold run, zig/qwen4_family.zig took over 6 minutes on Linux and over 3 on Windows. The top 8 binaries were about 70% of test running time. Most test binaries are built in Debug.
  • Compile-only jobs lose their cache too. zig-macos-dev-tools takes 13 minutes and waits longest for a runner. Pull requests save caches that no other pull request can read.

Plan

  1. CI: zig and zig-windows keep main's build cache, and the quick checks run first #2140: main saves the build cache and pull requests restore it, with the quick checks first.
  2. The same cache for release-paths and zig-macos-dev-tools.
  3. Build the heaviest test binaries in ReleaseSafe, where running them costs more than compiling them.
  4. Reuse a test's pass when its binary and every non-Zig file it could read are unchanged. In the 190 landings over the 4 days to 5 Nov, 92 touched the sources nearly every test imports, 76 changed other Zig and 22 changed no Zig.

Every check keeps running on main whenever the shared sources change, because main then starts from an empty cache. #2172 measures item 4 and traces which files tests read at run time. A changed fixture must never reuse an old pass.

Activity

  1. ashhart commented on Nov 5, 2026

    @ashhart
    OwnerAuthor

    Thanks @devopsfortunato. Item 1 landed as #2140. If you're picking up one of items 2 to 4, please say which here, so the work doesn't overlap.

  2. ashhart commented on Nov 5, 2026

    @ashhart
    OwnerAuthor

    What the probes on #2172 found, with a correction.

    Why a restored cache didn't help:

    1. The runner's CPU. Host code compiles for each runner's own CPU, a compile's cache key carries that CPU, and GitHub's runners differ. On a small target, restored on other runners, 1 of 4 hit on Linux (the one with the same CPU) and 0 of 4 on Windows. Compiling for a fixed x86_64_v3 hit 8 of 8, in 2-7 s instead of 108-263 s. The fix goes into CI: main's build cache fits every runner, and release-paths and macOS use it too #2252 after the 1.2.3 cut.
    2. Test runs always repeat after a restore. Zig 0.17's cache misses on any change to a declared input's file details, even when the contents match, and a restore always changes a test binary's. A small patch to the build runner's copy of that check fixes it. That change comes after CI: main's build cache fits every runner, and release-paths and macOS use it too #2252, along with declared fixtures and a cold main.
    3. C and C++ sources always recompile on a fresh checkout. This is the same check, but inside the compiler binary, which the patch can't reach. A touched .zig source with the same contents stays cached; a touched .c source recompiles. On Windows that's 13 test compiles and 8 small libraries, about 2.4 minutes of summed compile time, and it reruns those tests, including qwen4_family. This one stays for now.

    Correction: I said on #2140 that the misses came from zig's global cache not being restored. That was wrong on CI: setup-zig already keeps it inside .zig-cache, so the ZIG_GLOBAL_CACHE_DIR line #2140 added does nothing. #2252 removes it.

    Whole suite on the fixed CPU (run 42038532226):

    zig build test Linux Windows
    Tests passed (skipped) 7,127 (59) 6,576 (141)
    Cold 701 s 1,475 s
    Restored on a different CPU, every test rerun not measured 329 s
    Restored, with the patch, nothing changed not measured 267 s

    That's one run of each, and the Windows restore landed on a newer CPU than the save, so the ratio is a guide rather than a promise. A pull request that changes the shared sources still recompiles nearly everything.

  3. ashhart commented on Nov 6, 2026

    @ashhart
    OwnerAuthor

    All four items are on main: the build cache on main with the quick checks first (#2140); the same cache for release-paths and the macOS dev tools, compiled for one CPU so it hits on every runner (#2252); the heaviest test gates in ReleaseSafe (#2249, thanks @devopsfortunato); and pull requests reusing main's test passes and compiles, C and C++ included (#2274). That last one also covers the C and C++ recompiles we said would stay. Main still checks every commit from scratch. #2311 is the one follow-up still open. If a pull request's zig or zig-windows job still reruns tests its diff can't reach, please open a new issue with the run link.

  4. ashhart commented on Nov 8, 2026

    @ashhart
    OwnerAuthor

    Thanks, @devopsfortunato. All four items are on main: the build cache with the quick checks first (#2140), the same cache on every runner (#2252), the heaviest gates in ReleaseSafe (#2249, yours), and pull requests reusing main's test passes (#2274). The follow-up #2311 has landed too. Closing. If a job still reruns tests its diff can't reach, please open a new issue with the run link.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions