Skip to content

Reduce typemap signature encoder allocations - #12601

Closed
simonrozsival wants to merge 2 commits into
simonrozsival-incremental-typemap-emissionfrom
simonrozsival-typemap-emission-optimizations
Closed

Reduce typemap signature encoder allocations#12601
simonrozsival wants to merge 2 commits into
simonrozsival-incremental-typemap-emissionfrom
simonrozsival-typemap-emission-optimizations

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Summary

Cache the two fixed signatures repeated for every emitted peer (CreateInstance and the activation constructor), and let PEAssemblyBuilder accept a pre-encoded BlobHandle. This avoids rebuilding nested SRM signature callbacks while retaining the existing encoder and IL/max-stack paths.

This is the third PR in #12598 -> #12599 -> this PR and must remain based on simonrozsival-incremental-typemap-emission.

Measurements

Release net11.0 harness, three warmups per process. The primary comparison used 21 alternating parent/final pairs for Microsoft.Android.Ref.36 Mono.Android.dll (18 MiB, 8,820 peers) and 31 pairs for TestFixtures.dll.

Input / case Parent wall median Final wall median Parent allocations Final allocations
Mono.Android, changed 167.382 ms 170.434 ms 92.616 MiB 90.754 MiB (-2.0%)
Mono.Android, unchanged 112.035 ms 112.723 ms 61.196 MiB 61.196 MiB
TestFixtures, changed 2.913 ms 2.963 ms 2.573 MiB 2.542 MiB (-1.2%)
TestFixtures, unchanged 1.763 ms 1.796 ms 1.575 MiB 1.575 MiB

The small wall deltas are within observed process noise: the unchanged path does not execute the changed emission code but shows the same-sized delta. A separate 102-iteration in-process real-input comparison measured changed emission at 102.618 ms parent vs 102.632 ms final (effectively neutral), with allocations at 89.927 MiB vs 89.458 MiB.

Allocation traces over three changed real-input iterations showed sampled Action<ParametersEncoder> allocations falling from 9.46 MiB to 2.54 MiB and Action<BlobEncoder> from 7.91 MiB to 4.45 MiB. CPU traces showed no attributable wall-time regression.

Rejected experiments

  • Direct task-owned file streaming: reduced managed allocations by about 4.8 MiB, but repeated task-equivalent comparisons regressed wall time by roughly 9-19%. A 64 KiB BufferedStream did not recover the regression. Reverted, including staging/atomic-replacement code.
  • Combined incremental/content fingerprint serialization: reduced allocations, but computing both fingerprints before the unchanged decision regressed paired unchanged wall time by about 13% on Mono.Android and 19% on TestFixtures. Reverted to the parent serialization and MVID path.
  • Base-constructor signature cache: a CPU trace attributed 2.47% inclusive time to CollectBaseConstructorChain, but the generic-context-safe cache produced no consistent real-input scan improvement and added about 0.012 MiB scan allocations. Reverted.
  • PEStreamOptions.PrefetchMetadata: real changed median regressed from 249.055 ms to 264.390 ms; unchanged regressed from 128.780 ms to 227.844 ms, with effectively unchanged allocations. Reverted.
  • Broader IL callback/state rewrite: not retained; traces supported the narrower fixed-signature cache without requiring a broad emitter rewrite.

Output identity

Parent and final aggregate SHA-256 hashes match:

Input Typemap assemblies Java sources
Mono.Android.dll D991AC653F162B8E7B461BE66CCB1643A6B0354452718CFE91222E6EA3B47D42 E4D683D59AEF99603D8BAD51248A693168A7E71894556F3DD4493BAFA62726A7
TestFixtures.dll 1EA6E805D6375F0C5C726A3281CE085D26679CBE3F467ACE9517F7EE1B43F6A1 D4A4A1CF9D4C4848CD97D9CCE1E2996DD8CAF1EF82E084E28FA9C4A83F621E41

Validation

  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -c Release -v minimal: 780 passed
  • Added focused coverage for the pre-encoded signature path.
  • Full-build TrimmableTypeMap.IntegrationTests were not run because neither bin/Debug/dotnet/dotnet nor bin/Release/dotnet/dotnet exists in this workspace.

simonrozsival and others added 2 commits August 31, 2026 14:43
Cache fixed activation and CreateInstance signatures so repeated PE emission avoids nested signature encoder delegates while preserving emitted bytes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build the cached signatures through the existing SRM encoder so the optimization stays small and preserves the established encoding path.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 31, 2026 12:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity

New issues introduced by this change (1)
Severity Finding
Medium severity tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​TypeMapAssemblyGeneratorTests.cs⚠️ Testing — This pre-encoded signature test doesn’t cover the main regression scenario from the…
What changed in this PR

This PR reduces allocations in the trimmable typemap PE emitter by caching two fixed method signatures that are emitted repeatedly (CreateInstance and the activation .ctor), and by extending PEAssemblyBuilder APIs to accept pre-encoded BlobHandle signatures so signature encoding can be done once and reused.

Changes:

  • Cache and reuse the CreateInstance and activation-constructor signatures inside TypeMapAssemblyEmitter.
  • Add PEAssemblyBuilder overloads to emit method bodies and member refs using a pre-encoded BlobHandle signature.
  • Add a unit test validating that a pre-encoded signature round-trips correctly through emission and metadata decoding.
File Description
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​TypeMapAssemblyGeneratorTests.cs Adds coverage for emitting methods using a pre-encoded signature blob.
src/​Microsoft.Android.Sdk.TrimmableTypeMap/​Generator/​TypeMapAssemblyEmitter.cs Caches two frequently-reused signature blobs to reduce repeated encoder allocations.
src/​Microsoft.Android.Sdk.TrimmableTypeMap/​Generator/​PEAssemblyBuilder.cs Introduces overloads that accept pre-encoded BlobHandle signatures for emission and member refs.

Comment on lines +420 to +423
encoder => {
encoder.OpCode (ILOpCode.Ldnull);
encoder.Return (returnsValue: true);
});
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.

2 participants