Skip to content

Cache type lookups in MasterFisher - #1633

Open
octoshikari wants to merge 1 commit into
FHIR:masterfrom
octoshikari:perf/cache-type-fishing
Open

Cache type lookups in MasterFisher#1633
octoshikari wants to merge 1 commit into
FHIR:masterfrom
octoshikari:perf/cache-type-fishing

Conversation

@octoshikari

Copy link
Copy Markdown

Problem

MasterFisher.fishForFHIR is called with only Type.Type from some very hot paths. The biggest one is ElementDefinition.isPrimitive, which runs for every child element of every instance being validated and fishes the type's StructureDefinition just to read its kind. 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, isPrimitive accounted for 36% of the total run time and package database lookups for 47%.

Change

Cache fishForFHIR results when the only requested type is Type.Type. That case can only ever be answered by the FHIR definitions — Package.internalFish has an explicit "Package doesn't currently support types" branch and FSHTank.internalFish skips 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:

IG before after
HL7/fhir-mCODE-ig 17.4s 11.3s
HL7/fhir-sdoh-clinicalcare 19.6s 14.6s

For both IGs the generated fsh-generated tree is byte-identical to the one produced without this change.

Tests

Two tests in MasterFisher.test.ts: repeated Type.Type fishing reaches the FHIR definitions only once, and fishing for other types is still not cached.

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
octoshikari force-pushed the perf/cache-type-fishing branch from aa25fcf to 58b802f Compare August 18, 2026 19:57
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.

1 participant