Skip to content

Compute compilation-wide facts once per edit instead of once per type (S5) - #8532

Merged
Aaronontheweb merged 1 commit into
feature/serialization-v2-generator-typekeyfrom
feature/serialization-v2-generator-facts
Sep 11, 2026
Merged

Aaronontheweb merged 1 commit into
feature/serialization-v2-generator-typekeyfrom
feature/serialization-v2-generator-facts

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Stack, bottom to top: S1 #8525 -> S2 #8526 -> S3 #8527 -> object elements #8528 -> S4 #8530 -> S5 #8532 -> S6 #8533 -> F1 #8534 -> F2 #8536 -> F3 #8537 -> S7 #8538. Each PR is one commit on top of the one below it. This PR is S5.

What changes

Nothing in output or diagnostics. Every keystroke got 4 to 8 times cheaper.

  • The known-types table is built once per compilation. The generator looks up about fifteen well-known types by name to classify fields. Before, every attributed type's step rebuilt that table on every edit: about 7,500 lookups per keystroke in a project with 500 messages. Now a weak table keyed on the compilation instance computes it once.
  • A facts stage. A new step, CompilationFacts, computes what the pipeline needs to know about the whole compilation, once per edit, and hands it down as plain values that compare by content. The protocol-coverage check (AKKASG029) reads its result instead of walking every type once per serializer.
  • Groundwork for Decision 19. The stage also records which referenced assemblies reference Akka.Serialization.V2. That is the filter the later implementor walk uses. A new scenario proves the filter is live. The walk itself is a skeleton with the right signature.
  • Not wired into resolve yet. Nothing the facts compute today changes a resolved serializer, so wiring them in would add risk for no gain. F1 does the wiring when it needs it.

Two small helpers were needed: an ImmutableArray<T> compares by reference, so a dictionary whose values are arrays needs a compare that looks inside.

Scenarios

Scenario d, a keystroke in an unrelated file, is this PR's pin: the facts stage runs and reports its output unchanged.

Scenario Facts stage
a. unrelated file edited Unchanged
b. field renamed Unchanged
c. comment added Unchanged
d. trivial keystroke Unchanged
e. unused reference added Unchanged
e2. reference that itself references V2 added Modified

Savings

Base is the S4 tip, measured in a paired run on the same machine. This PR's row was measured twice in separate runs: allocations were identical, and means agreed within 5 percent.

Row Base (S4) This PR Change
Fresh driver, full corpus 78 ms, 21.9 MB 22 ms, 18.1 MB 3.5x faster, -18% allocated
Warm driver, one field renamed 65 ms, 12.5 MB 14 ms, 8.6 MB 4.8x faster, -31% allocated
Warm driver, comment edit 59 ms, 6.8 MB 8.8 ms, 3.0 MB 6.7x faster, -56% allocated
Warm driver, unrelated file edited 53 ms, 6.7 MB 6.5 ms, 2.9 MB 8.1x faster, -57% allocated

The S1 baseline blamed the no-change floor on Roslyn re-running the per-type step. Most of that floor was ours. A keystroke in an unrelated file now costs about 6 ms and 3 MB at 500 messages.

How it was checked

313 tests pass (309 plus 3 facts tests and 1 scenario). Generator builds with warnings as errors. Akka.Remote builds. Golden, wire, and both model snapshots unchanged.

@Aaronontheweb Aaronontheweb added tests serialization akka.net v1.6 Akka.NET v1.6-related issues labels Sep 9, 2026
@Aaronontheweb
Aaronontheweb added this pull request to stack #8531 September 9, 2026 13:28
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-generator-facts branch from 89ab9e9 to 68e2a4c Compare September 9, 2026 13:31
@Aaronontheweb Aaronontheweb changed the title Serialization.V2: compilation-facts stage; KnownTypes once per compilation (S5) Compute compilation-wide facts once per edit instead of once per type (S5) Sep 9, 2026
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-generator-facts branch from 68e2a4c to be67858 Compare September 9, 2026 17:14
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-generator-facts branch from be67858 to 96f9017 Compare September 9, 2026 18:08
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-generator-facts branch 2 times, most recently from 56fd382 to 8f64d5f Compare September 10, 2026 23:44
…ation; referenced-assembly walk skeleton

S5 of the generator architecture pass:

- Add CompilationFacts (symbol-free, value-equatable): per protocol key, the sorted local
  unmarked-implementor list (AKKASG029's input) and a Decision-19 referenced-assembly
  implementor list (empty for now), plus the sorted list of referenced assemblies that
  themselves reference Akka.Serialization.V2.
- Add the CompilationFacts pipeline stage (context.CompilationProvider.Combine(collected
  serializers).Select(...), tracking name CompilationFacts) in a new
  AkkaSerializerGenerator.Facts.cs. The whole-compilation local-implementor walk now runs
  once per compilation change for every serializer's protocol at once, instead of once per
  serializer inside the old ValidateProtocolCoverage.
- ValidateProtocolCoverage is now a pure function of SerializerInfo + CompilationFacts, with
  no Compilation parameter; the AKKASG029 coverage output combines ResolvedSerializers with
  CompilationFacts instead of the raw Compilation. Same diagnostic id/text/trigger conditions.
- CompilationFacts is deliberately NOT combined into ResolvedSerializers' own inputs yet:
  nothing it computes today affects resolve/emission, so wiring it in now would only add
  equality-risk surface. Documented at TrackingNames.CompilationFacts for when Decision 19's
  referenced-assembly implementor walk becomes real.
- Cache KnownTypes per Compilation instance via a ConditionalWeakTable instead of rebuilding
  it (about fifteen GetTypeByMetadataName lookups) once per attributed type per compilation
  change in both attribute transforms.
- Add the Decision 19 referenced-assembly walk skeleton: filters referenced assemblies down
  to those that reference Akka.Serialization.V2 (real), with the implementor enumeration
  itself returning empty (a follow-up).
- Extend TrackingNames.All and the incremental scenario specs with the new stage; add
  GeneratorCompilationFactsSpec for the facts computation itself.

Benchmark (SourceGeneratorBenchmarks, ShortRun, base b441744 vs this branch):
  Fresh driver, full corpus:            78.47 ms / 21.92 MB  ->  20.82 ms / 18.07 MB
  One field renamed in one message:     65.46 ms / 12.49 MB  ->  13.06 ms /  8.64 MB
  Trailing whitespace/comment edit:     58.98 ms /  6.75 MB  ->   8.90 ms /  2.93 MB
  Unrelated file edited (no-change):    52.80 ms /  6.73 MB  ->   6.81 ms /  2.91 MB

All 313 Akka.Serialization.V2.Tests pass (309 plus 4 new); GoldenOutput/WireSnapshots/
MessageModelSnapshots unchanged; generator builds with -warnaserror; Akka.Remote builds.
@Aaronontheweb
Aaronontheweb force-pushed the feature/serialization-v2-generator-facts branch from 8f64d5f to 3e27a51 Compare September 11, 2026 15:05
@Aaronontheweb
Aaronontheweb merged commit d1cfd05 into dev Sep 11, 2026
16 of 17 checks passed
@Aaronontheweb
Aaronontheweb deleted the feature/serialization-v2-generator-facts branch September 11, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

akka.net v1.6 Akka.NET v1.6-related issues serialization tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant