Skip to content

Reuse generated stunts across builds and projects while their types keep their shape - #318

Merged
kzu merged 1 commit into
mainfrom
feat/stunts-cache
Oct 27, 2026
Merged

kzu merged 1 commit into
mainfrom
feat/stunts-cache

Conversation

@kzu

@kzu kzu commented Oct 27, 2026

Copy link
Copy Markdown
Member

Rebuilds where the types behind a stunt haven't changed (no new type definitions or member signatures) now reuse the code generated by an earlier build instead of running the processor pipeline again. Projects in the same repository share the cache, so test projects using the same core interfaces generate them only once.

How it works

  • Where: Stunts.targets sets the compiler-visible StuntsCacheDir to .vs/stunts-cache/ at the git repository root. The root comes from the git SourceRoot that InitializeSourceControlInformation resolves, the same approach as ThisAssembly.Git. If there is no git root it uses the solution directory, then obj/stunts-cache/. Set EnableStuntsCache=false to turn the cache off.

  • Key (StuntFingerprint): a SHA256 over:

    • the generator version (MVIDs of the generator, Roslyn and processor assemblies);
    • the compilation-wide inputs processors read (configuration, language version, nullable, unsafe and metadata import options, culture, corlib, and optional attribute lookups);
    • the declaration of each stunt type, including inherited members, attributes and overridden members.

    Types used in signatures contribute only their shape, not their members. Assemblies contribute only their relation to the compilation (self, friend or other), so rebuilt or re-versioned libraries keep their entries.

  • Storage (StuntCache):

    • one content-addressed .stunt file per key, in a folder per generator version;
    • written atomically (temp file, then rename);
    • a bounded in-memory copy per compiler server;
    • pruned at most once a day.
  • Replay: cached entries replay ST006/ST015 and the unchanged-scaffold skip. Default interface implementation classes are cached under their own keys. Registration still runs every time, so delegate and nested stunts get their forwarders from the cached text.

  • Caching is disabled when processors from other assemblies are configured, since their inputs are unknown.

Numbers (400 metadata stunts, generator only)

Run Time
No cache 1.91s
Empty cache (first build) 1.95s
Disk hit 0.41s
Memory hit (same compiler server) 0.06s
Another project, warm disk cache 0.09s

Tests

StuntCacheTests (11 tests) covers:

  • hits, and signature changes;
  • sharing across projects, and friend assemblies;
  • nested and delegate stunts, and default implementations;
  • diagnostics replay, and damaged entries;
  • culture changes, and opting out without a directory or with custom processors.

The full unit suite passes (368 tests). I checked StuntsCacheDir resolution with scratch projects in a repository, in a worktree, outside git, with SolutionDir, and with the cache disabled.

This pull request was created by CodeAlta, an AI coding agent.

@kzu kzu added the enhancement New feature or request label Oct 27, 2026
…eep their shape.

Stunts are cached in .vs/stunts-cache at the git root (from source control information), keyed by a fingerprint of the generator, the compilation-wide inputs processors read, and the declarations and member signatures of the stunt's types. Unchanged rebuilds skip processing entirely, and test projects consuming the same interfaces share the entries.

Co-authored-by: CodeAlta <noreply@codealta.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
@kzu
kzu force-pushed the feat/stunts-cache branch from 60af060 to aa3a182 Compare October 27, 2026 02:20
@kzu
kzu enabled auto-merge (rebase) October 27, 2026 02:21
@kzu
kzu merged commit 4ac4dd2 into main Oct 27, 2026
6 checks passed
@kzu
kzu deleted the feat/stunts-cache branch October 27, 2026 02:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant