Cache type lookups in MasterFisher - #1633
Open
octoshikari wants to merge 1 commit into
Open
Conversation
MasterFisher.fishForFHIR gets called with only Type.Type from hot paths like ElementDefinition.isPrimitive, which asks whether the same handful of type codes are primitives over and over. Every one of those calls runs a query against the package database and may re-read the resource JSON from disk. Only the FHIR definitions can answer a Type.Type fish -- both Package and FSHTank skip that type -- and the definitions don't change during export, so the results can be cached for the life of the fisher. Building HL7/fhir-mCODE-ig goes from 17.4s to 11.8s and HL7/fhir-sdoh-clinicalcare from 19.7s to 14.5s, with byte-identical output in both cases.
octoshikari
force-pushed
the
perf/cache-type-fishing
branch
from
August 18, 2026 19:57
aa25fcf to
58b802f
Compare
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.
Problem
MasterFisher.fishForFHIRis called with onlyType.Typefrom some very hot paths. The biggest one isElementDefinition.isPrimitive, which runs for every child element of every instance being validated and fishes the type's StructureDefinition just to read itskind. The same handful of type codes (string,code,uri, ...) end up being looked up thousands of times per run, and each lookup is a query against the package database plus a possible read of the resource JSON from disk.In a CPU profile of a build of HL7/fhir-mCODE-ig,
isPrimitiveaccounted for 36% of the total run time and package database lookups for 47%.Change
Cache
fishForFHIRresults when the only requested type isType.Type. That case can only ever be answered by the FHIR definitions —Package.internalFishhas an explicit "Package doesn't currently support types" branch andFSHTank.internalFishskips the type as well — and the definitions don't change while fishing, so the answer is stable for the life of the fisher. Everything else is fished exactly as before, so results that can change as the package fills up are not cached.Effect
Best of five runs with a warm package cache, node 24:
For both IGs the generated
fsh-generatedtree is byte-identical to the one produced without this change.Tests
Two tests in
MasterFisher.test.ts: repeatedType.Typefishing reaches the FHIR definitions only once, and fishing for other types is still not cached.