Repository navigation
Commit a25009d
committed
fix(core): intern hash instructions in a pool and plan with id lists (#36249)
Hash plans are `HashMap<task, Vec<HashInstruction>>` — every task owns
deep copies of every instruction in its dependency closure. The
distinct-instruction population is only a few thousand values (a couple
per project × input), but plans store them O(tasks × closure) times:
~1.1M copies × ~200 bytes on a 1,110-task benchmark with deep closures.
This is the structure that #35071's sibling-inputs correctness fix
legitimately inflated (the real mechanism behind NXC-4605's +91 MiB),
and it exists in every parallel DTE agent process (#36152).
Plans store `u32` ids into an `InstructionPool` interner and travel with
it as `HashPlans` through the planner→hasher `External`:
- The interner's entry()-serialized id allocation guarantees value-equal
⇒ id-equal, so integer sort+dedup ≡ value-level dedup.
- Dependency subtrees are memoized per (project, propagated input) as id
lists and spliced into plans as integer memcpy — zero materialization on
the O(tasks × closure) path. Deps-outputs subtrees and cyclic graphs use
the existing per-task traversal, interned at the boundary.
- `task_hasher` / `hash_plan_inspector` resolve ids in place; the
string-returning `getPlans` API materializes and Ord-sorts, keeping
observable behavior identical.
**Measured** (densified bench: 1,110 tasks, avg closure ~555, 3 runs):
planning 373 ms → 144 ms (~2.6×), peak process RSS 947 MB → 658 MB
(−31%), `hash_plans` unchanged. Task hashes byte-for-byte identical — 72
TS hasher+planner tests and all Rust tests pass. Negative control: the
same memo returning materialized instructions measured 2.8× slower; the
representation change is the entire win.
> [!NOTE]
> **Stacked on #36248** (uses its `VisitedTracker`); retarget to
`master` once that merges. Draft pending: full e2e sweep, a committed
`bench:plan` variant (the stock benchmarks workspace has no dependency
inputs and cannot exercise this path), scoped clone of the pool `Ref` in
the Runtime hash arm, and a proper opaque d.ts type name.
Fixes NXC-4607
<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/NXC-4603-Workspace-Fileset-Cache-Memory-Fix-69f28e06)
<!-- polygraph-session-end -->
---------
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Backport notes:
- 22.7.x builds `external_deps_mapped` per get_plans call rather than
memoizing it on the planner, so the struct keeps no such field here. The
subtree memo is still safe: its values derive only from the project graph
and nx_json, which are immutable either way.
- `memoized_dep_subtree` and `compute_dep_subtree` take 22.7.x borrowed
`hashbrown::HashMap<&String, Vec<&String>>` instead of the owned map.
- `OnceCache` comes from #36244, which is otherwise skipped: 22.7.x has no
workspace fileset cache for that PR to fix. Only the type is taken.1 parent c885d84 commit a25009d
6 files changed
Lines changed: 389 additions & 75 deletions
File tree
- packages/nx/src/native/tasks
- hashers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
| 43 | + | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
| 47 | + | |
46 | 48 | | |
| 49 | + | |
47 | 50 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
| 51 | + | |
53 | 52 | | |
54 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
| |||
88 | 89 | | |
89 | 90 | | |
90 | 91 | | |
91 | | - | |
| 92 | + | |
| 93 | + | |
92 | 94 | | |
93 | 95 | | |
| 96 | + | |
94 | 97 | | |
| 98 | + | |
95 | 99 | | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
102 | | - | |
103 | | - | |
104 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
105 | 106 | | |
106 | 107 | | |
107 | 108 | | |
| |||
0 commit comments