Repository navigation
Conversation
|
@codex review |
The entry names #9190, so the changelog points at this PR.
Changelog💅 Polish
Semantic Diff1 commit · Full page Remember type path lookups within a compilation3096465 · JonoPrest · Nov 03, 2026, 07:59 UTC
A direct-mapped table now remembers successful lookups and unchanged normalizations. An entry is only reused while the environment tables the lookup read are physically the same (env.types for a Pident, env.components for a Pdot, env.modules and env.components for a normalization), and only within the request that stored it. Changed normalizations are computed again, so paths in types are allocated as before. Paths with a functor application aren't cached, the memo is skipped while loading .cmi files is disabled, and it is emptied when a request ends and when a loaded module is replaced. The two tables start empty and grow from 64 to 4096 slots as entries are stored, and a replaced entry is updated in place, so a small compilation allocates little for them: fixed 4096 slot tables made each runtime file allocate about 3% more words than master. Now the file that grows most, Dom.res, allocates 0.24% more, and the runtime and Belt in total 0.06% less. On the corpus 94% of lookups and 96% of normalizations hit, and the output is byte identical to master. Review
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9190 +/- ##
==========================================
+ Coverage 81.93% 81.98% +0.05%
==========================================
Files 456 457 +1
Lines 65517 65699 +182
==========================================
+ Hits 53679 53862 +183
+ Misses 11838 11837 -1
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
The entry names #9190, so the changelog points at this PR. Signed-off-by: Jono Prest <jjprest@gmail.com>
06d84a7 to
d5690b3
Compare
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9190 |
27cb0bf to
4bb2b5d
Compare
🎉 Compiler benchmarksAllocated wordsIn total: 683,552,688 → 683,045,157 (-0.07%).
160 more changes
InstructionsIn total: 9,664,672,297 → 9,630,314,898 (-0.36%). 🎉 Improvement: 4 benchmarks execute more than 1% fewer instructions.
11 more changes
Output sizeIn total: 25,569,598 → 25,571,454 (+0.01%). None of the 13 benchmarks changed by 0.1% or more. Pull request 3096465 compared with base b82d944 in this run. |
3096465 to
5cf3317
Compare
Changelog💅 Polish
Semantic Diff1 commit · Full page Remember type path lookups within a compilation6af9e8f · JonoPrest · Nov 03, 2026, 07:59 UTC
A direct-mapped table now remembers successful lookups and unchanged normalizations. An entry is only reused while the environment tables the lookup read are physically the same (env.types for a Pident, env.components for a Pdot, env.modules and env.components for a normalization), and only within the request that stored it. Changed normalizations are computed again, so paths in types are allocated as before. Paths with a functor application aren't cached, the memo is skipped while loading .cmi files is disabled, and it is emptied when a request ends and when a loaded module is replaced. The two tables start empty and grow from 64 to 4096 slots as entries are stored, and a replaced entry is updated in place, so a small compilation allocates little for them: fixed 4096 slot tables made each runtime file allocate about 3% more words than master. Now the file that grows most, Dom.res, allocates 0.24% more, and the runtime and Belt in total 0.06% less. On the corpus 94% of lookups and 96% of normalizations hit, and the output is byte identical to master. Review
|
🎉 Compiler benchmarksAllocated wordsIn total: 682,019,210 → 681,511,242 (-0.07%).
160 more changes
InstructionsIn total: 9,666,377,165 → 9,633,507,777 (-0.34%). 🎉 Improvement: 4 benchmarks execute more than 1% fewer instructions.
12 more changes
Output sizeIn total: 25,571,598 → 25,573,568 (+0.01%).
Pull request 6af9e8f compared with base 145df8d in this run. |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9190 |
069bcf9 to
f79567f
Compare
Why:
Typechecking a 552 module React app calls Env.find_type_full 1.76M
times, and normalize_path 666k times from expand_abbrev. Most calls
look up the same few paths against the same environment: `option`
alone is 528k of them, followed by `string`, `array`, `Jsx.element`.
Each lookup walks the open layers of env.types, and expanding a type
that isn't an abbreviation also normalizes its path, which raises and
catches Not_found to return the path unchanged.
A direct-mapped table now remembers successful lookups and unchanged
normalizations. An entry is only reused while the environment tables the
lookup read are physically the same (env.types for a Pident, env.components
for a Pdot, env.modules and env.components for a normalization), and only
within the request that stored it. Changed normalizations are computed
again, so paths in types are allocated as before. Paths with a functor
application aren't cached, the memo is skipped while loading .cmi files is
disabled, and it is emptied when a request ends and when a loaded module is
replaced.
The two tables start empty and grow from 64 to 4096 slots as entries are
stored, and a replaced entry is updated in place, so a small compilation
allocates little for them: fixed 4096 slot tables made each runtime file
allocate about 3% more words than master. Now the file that grows most,
Dom.res, allocates 0.24% more, and the runtime and Belt in total 0.06% less.
On the corpus 94% of lookups and 96% of normalizations hit, and the output
is byte identical to master.
Review: Can an entry be stale? The tables an entry depends on are
immutable, so the same physical table gives the same answer. New unit
tests bind the same path differently in two environments within one
request (recursive type placeholders, recursive module approximations,
an alias) and fail if any of these checks is removed.
Review: Can growing or reusing an entry return a wrong result? Growth moves
each entry to the slot of its path in the larger table, and a lookup still
checks the path, the epoch and the tables of each entry it finds. An entry
is only referenced from its slot, so updating it in place changes no result
a caller holds. A unit test looks up 3000 types across the growth and fails
if a replaced entry keeps its old result.
Signed-off-by: Jono Prest <jjprest@gmail.com>
f79567f to
6af9e8f
Compare
Typechecking a 552 module React app calls
Env.find_type_full1.76M times. Most of those resolve the same few paths against the same environment:optionalone is 528k calls, thenstring,array,Jsx.element. Each one walks the open layers ofenv.types. On top of that, expanding a type that isn't an abbreviation (a variant, record or abstract type) normalizes its path, which raises and catchesNot_foundto return the path unchanged, 666k times.This adds a small direct-mapped memo for both, per request:
env.typesfor aPident,env.componentsfor aPdot, andenv.modulesplusenv.componentsfor a normalization. These tables are immutable, so the same table gives the same answer..cmifiles is disabled, and it is emptied when a request ends or a loaded module is replaced.On the corpus below, 94% of lookups and 96% of normalizations hit.
Clean builds, 8 vCPU Linux VM, median of interleaved runs:
The gain is on code that leans on many small type lookups. Backend code without JSX doesn't change.
Testing:
ounit_type_path_memo_tests.mlbinds the same path differently in two environments within one request (recursive type placeholders, a recursive module approximation, an alias). Each test fails if its check is removed from the memo.type_path_lookup_test.rescovers shadowing, local opens, aliases, functors, first class modules, recursive types and recursive modules. Its output matches master..cmi,.cmj,.astand.cmtare byte identical to master across three production codebases and their dependencies.make testandmake test-rewatchpass.