ci: speed up the integration setup step - #695
Conversation
0099ce2 to
3bf9f8c
Compare
MeasurementsBaseline is the last five
The median moves by 27 seconds. The range is the more useful column: the two On the Go cache, a correction to an earlier version of this comment. It first reported That is inherent to what is being cached. cri-o is cloned from its Job totals: |
|
JOBS |
tests | parallel pass | per test | serial pass | |
|---|---|---|---|---|---|
main 33016232632 |
4 | 306 | 25.6m | 5.0s | 1.4m |
| run 2 33205171374 | 4 | 315 | 31.1m | 5.9s | 1.6m |
| run 3 33210235032 | 8 | 315 | 19.4m | 3.7s | 1.4m |
| run 4 33212882666 | 16 | 315 | 18.3m | 3.5s | 1.3m |
nproc * 2 is where the win is: against run 2, which ran the same 315 tests, 31.1m to 19.4m, a 38% cut. cri-o (0) as a whole went 35.7m to 25.5m.
nproc * 4 was tried and rejected. It bought 1.1 further minutes, which is well inside the noise -- the two JOBS=4 runs above differ by 5.5 minutes on their own -- and there is no reason to run sixteen container test suites at once on a four-core runner for a difference that cannot be distinguished from variance. The multiplier stays at 2.
The caveat from the previous comment still applies: three runs is not a distribution, and this needs watching after it merges. What makes it credible for now is that the JOBS=8 gain (11.7m) is twice the spread seen between two identically configured runs, and that per-test cost fell from 5.9s to 3.7s while the serial pass, which is unaffected by JOBS, stayed flat at 1.3-1.6m across all four.
Two other things worth recording, both arguing for more samples rather than fewer:
- Setup was 138s in run 2, 162s in run 3 and 167s in run 4, on a confirmed cache hit every time. Runner variance, not the cache.
make -C "$CRIO_DIR" all test-binariestook 0.3m in run 2 and 1.6m in run 3, also on a cache hit. cri-o is cloned from itsmain, so it moves between runs and the Go cache goes progressively stale against it. Either way it is about a minute, and not the thing worth optimising.
199b413 to
b84710d
Compare
The cri-o clone is the only one in this script without a depth limit, and it is by far the largest repository of the lot. Only the default branch's tree is used -- scripts/versions, the test suites, and the vendored ginkgo -- so its history is fetched and thrown away on every CI run. Most of the time that is not expensive: across fifteen jobs on main the clone took a median of 12 seconds, against 4 with --depth 1. The tail is what hurts. Two of those fifteen took 166 and 254 seconds, and the worse of them stretched the whole setup step to 415 seconds, against a median of 180. Nine jobs into this branch the step has stayed within a 103 to 166 second band. While at it, fetch the bats tag directly instead of cloning everything and checking the tag out afterwards. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
The setup script builds runc, ginkgo, critest and crictl from source, and the cri-o job then builds cri-o itself. All of that is Go, and all of it recompiles the standard library and the vendored trees from scratch on every run. setup-go's built-in cache is keyed on a go.sum this repository does not have, so cache GOCACHE and the module cache by hand instead. The key follows the dependency versions pinned in hack/github-actions-setup, so a bump gets a fresh cache; restore-keys still seeds it from the previous one, which keeps that first build incremental. The job name is part of the key so that the two jobs, which build different things, do not race each other for a single cache entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP78Qd5ev6FaneLGaqoWud Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
cri-o's test/test_runner.sh defaults JOBS to nproc, which is 4 on a GitHub-hosted runner, and that is where the bulk of the job goes: the parallel bats pass takes 25 to 31 minutes of a roughly 35 minute job, while the serial pass that follows it takes under two. The suite is not CPU-bound. Nearly all of that time is spent waiting for cri-o to come up, for container state to settle, and for stop timeouts to expire, so running more tests than there are cores mostly fills in idle time. Over the same 315 tests the parallel pass goes from 31.1 minutes at JOBS=4 to 19.4 at nproc * 2, taking the whole job from 35.7 minutes to 25.5, with no test failing or needing the retry that test_runner.sh already configures. nproc * 4 was measured as well and is not worth it: 18.3 minutes, a difference that does not stand out from the run-to-run spread, in exchange for sixteen concurrent container test suites on a four core runner. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
b84710d to
72f1a37
Compare
|
Instead of the last patch, I opened an upstream PR: cri-o/cri-o#10285 which is more natural. But let's keep it for now and we can revert it later once the above PR will land. |
Three changes:
Shallow-clone cri-o and bats. The cri-o clone is the only one in the script without a depth limit, and it is the largest repository involved. Only the default branch's tree is used (
scripts/versions, the test suites, and the vendored ginkgo), so the history is fetched and discarded every run. bats is switched from clone-then-checkout to fetching the tag directly.Cache GOCACHE and the module cache. The script builds runc, ginkgo, critest and crictl from source, and the cri-o job builds cri-o itself; every run recompiles the standard library and the vendored trees from scratch.
setup-go's own cache is keyed on ago.sumthis repository does not have, hence caching the two directories by hand. The key follows the versions pinned in the setup script, withrestore-keysso that a version bump still gets an incremental first build, and includes the job name so the two jobs do not race for one cache entry..github/zizmor.ymlcomes with it: zizmor'scache-poisoningaudit fires because a tag push can trigger this workflow, and a poisoned cache could then reach a published artifact. This workflow publishes nothing -- the release build is instatic.yml, which does not cache -- so the finding is ignored for this file only.Oversubscribe the cores for the cri-o suite.
test/test_runner.shdefaultsJOBStonproc, which is 4 on a hosted runner; the suite waits on cri-o startup, container state and stop timeouts far more than it computes, so it getsnproc * 2instead.nproc * 4was measured too and rejected: it is another 1.1 minutes, which does not stand out from the run-to-run spread.Overall this saves about 5-6 minutes, or about 15%. Detailed measurements are in the comments below.
🤖 Generated with Claude Code