Background
Per-class generated test sources defer heavy metadata construction behind lazy Entries_0 properties (registered via SourceRegistrar.RegisterEntries<T>, resolved during discovery with parallel pre-resolution in TUnit.Engine/Building/Collectors/AotTestDataCollector.cs). This already moves the JIT cost off the module-init path and spreads it across cores.
However, the Entries_0 builder bodies are the bulk of all generated IL: each one inlines large object-initializer blocks (TestEntry<T>, attribute arrays, filter data, etc.) repeated per test method across every test class. Tier-0 JIT time is roughly linear in IL size, so for large suites the discovery-time JIT cost scales with this repeated boilerplate.
Proposal
Move the repeated construction patterns into shared factory helper methods in TUnit.Core (JIT-compiled once, reused by every generated builder). Generated code then calls helpers passing only the per-test data (strings, constants, delegates) as arguments:
- A call with N args compiles to far less IL than N inline property-set sequences +
newobj chains.
- The per-test-unique parts (invoker delegates via
ldftn, string literals) stay in generated code — they are cheap.
- Helpers live in TUnit.Core so they can be tiered/optimized once instead of per-class.
Candidates to factor out (audit the emitted Entries_0 shape for the highest-volume patterns first):
TestEntry<T> construction with common-default fields
- Attribute array construction
- Filter-data field population
- Repeated metadata sub-object initializers
Expected impact
Smaller IL per generated builder → proportionally less tier-0 JIT time during discovery, especially on large suites and constrained CI agents. Also shrinks compile time and assembly size as a side effect.
Constraints
- AOT-compatible: helpers must be plain static methods, no reflection; keep
[DynamicallyAccessedMembers] annotations flowing where Type parameters are involved.
- No
.Collect()/fan-in changes to the incremental pipeline — this is purely an emit-shape change per file.
- Dual-mode: reflection mode unaffected, but any shared helper semantics must match what reflection mode produces.
- Snapshot tests will need re-verification across all TFM output dirs.
Measurement
Before/after with dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:0x10:4 → PerfView JitStats (per-method JIT ms, IL bytes jitted) on a large test suite.
Background
Per-class generated test sources defer heavy metadata construction behind lazy
Entries_0properties (registered viaSourceRegistrar.RegisterEntries<T>, resolved during discovery with parallel pre-resolution inTUnit.Engine/Building/Collectors/AotTestDataCollector.cs). This already moves the JIT cost off the module-init path and spreads it across cores.However, the
Entries_0builder bodies are the bulk of all generated IL: each one inlines large object-initializer blocks (TestEntry<T>, attribute arrays, filter data, etc.) repeated per test method across every test class. Tier-0 JIT time is roughly linear in IL size, so for large suites the discovery-time JIT cost scales with this repeated boilerplate.Proposal
Move the repeated construction patterns into shared factory helper methods in
TUnit.Core(JIT-compiled once, reused by every generated builder). Generated code then calls helpers passing only the per-test data (strings, constants, delegates) as arguments:newobjchains.ldftn, string literals) stay in generated code — they are cheap.Candidates to factor out (audit the emitted
Entries_0shape for the highest-volume patterns first):TestEntry<T>construction with common-default fieldsExpected impact
Smaller IL per generated builder → proportionally less tier-0 JIT time during discovery, especially on large suites and constrained CI agents. Also shrinks compile time and assembly size as a side effect.
Constraints
[DynamicallyAccessedMembers]annotations flowing whereTypeparameters are involved..Collect()/fan-in changes to the incremental pipeline — this is purely an emit-shape change per file.Measurement
Before/after with
dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:0x10:4→ PerfView JitStats (per-method JIT ms, IL bytes jitted) on a large test suite.