Skip to content

Cut the CI build time: analyzers once per project, cache NuGet - #3171

Merged
mgravell merged 1 commit into
mainfrom
marc/build-timing
Aug 10, 2026
Merged

Cut the CI build time: analyzers once per project, cache NuGet#3171
mgravell merged 1 commit into
mainfrom
marc/build-timing

Conversation

@mgravell

Copy link
Copy Markdown
Collaborator

The .NET Build step was ~4 minutes on the Windows runner. Measured with /p:ReportAnalyzer=true across all 42 compilations of a clean build: analyzers are 226s of CPU and ~40% of wall time, of which StyleCop is 155s and PublicApiAnalyzers 37s. Our own analyzer and generators are 5.2s - about 2% - so they are not the problem. Restore is another ~80s, uncached on both runners.

  • run analyzers on one TFM per project (the newest it builds) rather than once per TFM: every rule we run is TFM-agnostic, so a six-TFM library was paying for the same diagnostics six times. Generators are unaffected. /p:RunAnalyzers=true gets the full per-TFM sweep back.
  • StyleCop applies to src/ only; it was buying nothing in tests/ and toys/ that review does not.
  • cache ~/.nuget/packages in both CI jobs.
  • --no-build on the CI test steps, which were each re-entering the build for ~15s.
  • drop GeneratePackageOnBuild from RESPite.Benchmark, which packed a nupkg on every Release build; the pack step still publishes it.

Clean full build locally: 26.7s -> ~19s, Csc CPU 110-124s -> 70-83s.

Checklist

  • I fully and freely contribute this code in accordance with the project license (and am legally able to do so)
  • I take responsibility for this contribution's quality and correctness, including any portions produced with AI assistance (see CONTRIBUTING.md).

The .NET Build step was ~4 minutes on the Windows runner. Measured with
/p:ReportAnalyzer=true across all 42 compilations of a clean build: analyzers are
226s of CPU and ~40% of wall time, of which StyleCop is 155s and PublicApiAnalyzers
37s. Our own analyzer and generators are 5.2s - about 2% - so they are not the
problem. Restore is another ~80s, uncached on both runners.

- run analyzers on one TFM per project (the newest it builds) rather than once per
  TFM: every rule we run is TFM-agnostic, so a six-TFM library was paying for the
  same diagnostics six times. Generators are unaffected. /p:RunAnalyzers=true gets
  the full per-TFM sweep back.
- StyleCop applies to src/ only; it was buying nothing in tests/ and toys/ that
  review does not.
- cache ~/.nuget/packages in both CI jobs.
- --no-build on the CI test steps, which were each re-entering the build for ~15s.
- drop GeneratePackageOnBuild from RESPite.Benchmark, which packed a nupkg on every
  Release build; the pack step still publishes it.

Clean full build locally: 26.7s -> ~19s, Csc CPU 110-124s -> 70-83s.
@mgravell
mgravell merged commit 74bfe78 into main Aug 10, 2026
5 of 8 checks passed
@mgravell
mgravell deleted the marc/build-timing branch August 10, 2026 11:40
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.

1 participant