Index pre-generated types per assembly to avoid O(n) ExportedTypes scans (#189)#192
Merged
jeremydmiller merged 1 commit intomainfrom Apr 27, 2026
Merged
Conversation
…dTypes scans CodeGenerationExtensions.FindPreGeneratedType ran assembly.ExportedTypes .FirstOrDefault on every call, making each lookup O(n) in the entry assembly's exported-type count. Static-mode consumers (Marten, Wolverine) call the helper once per code file - storage providers, query handlers, projection types, event-store insert ops - so an app with 50 code files and a 1000-type entry assembly does ~50,000 type comparisons during cold start. Cache an indexed dictionary per assembly behind a ConditionalWeakTable so the hash isn't rooted (plugin / unloadable AssemblyLoadContext scenarios still work). First lookup builds the index in O(n); every subsequent lookup is O(1). Bump JasperFx to 1.27.0. Closes #189. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
3 tasks
jeremydmiller
added a commit
to JasperFx/marten
that referenced
this pull request
Apr 27, 2026
…ocations (#4295) JasperFx 1.27.0 (JasperFx/jasperfx#192) makes CodeGenerationExtensions.FindPreGeneratedType O(1) per call by caching an indexed dictionary per Assembly. Bumps the pin so static-mode cold start stops paying O(ExportedTypes) per ICodeFile.AttachTypes call. Two small Marten-side fixes alongside: * QuickEventAppender previously allocated a new Queue<long> per stream inside the foreach over WorkTracker.Streams, even though the quick-append path never reads from it (only applyRichMetadata dequeues; applyQuick doesn't touch it). Hoist a single throwaway instance out of the loop so bulk appends stop paying one allocation per stream. * DocumentSessionBase.operationDocumentTypes() did Operations().Select(...).Where(...).Distinct(), enumerating Operations() twice and chaining a fresh enumerator stack on every SaveChanges. Replace with a single-pass HashSet<Type> walk. Same observable behavior, fewer intermediate allocations on the hot save path. Companion to the Marten 9.0 cold-start umbrella issue (#4294); the non-breaking subset that didn't have to wait for 9.0. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CodeGenerationExtensions.FindPreGeneratedTyperanassembly.ExportedTypes.FirstOrDefaulton every call, making each lookup O(n) in the entry assembly's exported-type count.Static-mode consumers (Marten, Wolverine) call the helper once per code file — storage providers, query handlers, projection types, event-store insert ops — so an app with 50 code files and a 1000-type entry assembly does ~50,000 type comparisons during cold start.
This PR caches an indexed dictionary per
Assemblybehind aConditionalWeakTable(so the hash isn't rooted — plugin / unloadableAssemblyLoadContextscenarios still work). First lookup builds the index in O(n); every subsequent lookup is O(1).Bumps
JasperFxto 1.27.0.Closes #189.
Test plan
CodeGenerationExtensionsTests— 4 facts, all pass:CodegenTestssuite (305 tests) green — no regressionsNotes
🤖 Generated with Claude Code