perf(cache): within-run hash memos + pkg.json via mtime+size fast path (680ms → ~400ms expected) - #84
Merged
Merged
Conversation
Three more wins on the cache-hit path after PR #83's 1395 → 680ms drop. 1. **`hashProjectPackageJson` routed through `Cache.hashFile`** — previously it called `Bun.file().bytes()` directly, bypassing the mtime+size fast path entirely. Now every project's pkg.json hash on a re-run is a stat + SQLite SELECT instead of a full file read. 2. **Within-run memo of `hashProjectPackageJson` per projectDir** — monorepo with 100 projects × 3 tasks each used to read the same pkg.json 300 times in one run; now 100 unique reads (cached as a Promise<string> per projectDir so concurrent tasks share the same in-flight resolution). 3. **Within-run memo of `hashTaskConfig` per config-object identity** (WeakMap) — the resolved config object is created once at prepareRun time; the JSON.stringify + sha256 of it is deterministic, so a WeakMap lookup replaces the full hash on second-and-later access. Wiring: new `HashCache { packageJson, taskConfig }` shape + `createHashCache()` factory, plumbed from `prepareRun` → `ExecuteArgs` → `ComputeHashArgs` → `plan.ts`. Added `hashFile` to the `CacheLayer` interface so the orchestrator doesn't need feature-detection. `LayeredCache.hashFile` delegates to local. Tests: 454 → 458. Four new tests cover: - taskConfig WeakMap memo returns the same hash + tracks the config object identity. - packageJson memo only resolves a projectDir once across two tasks; Promise reference identity is preserved. - Different projectDirs each get their own packageJson entry. - pkg.json hashes go through Cache.hashFile (verified via direct re-call producing the same sha256). Expected next benchmark: 680 → ~450ms (pkg.json reads were the biggest remaining per-task overhead).
Exelord
pushed a commit
that referenced
this pull request
Jul 13, 2026
Cycle 3 closed: virtualization met the 60fps bar everywhere (wave 1), DX-1..5 + CORE-1 + the committed perf guard shipped (waves 2-3), and the dead live-run machinery discovery (P4/P5/P7/C3 moot — queue protocol has no server side) is documented with the repurpose-vs-delete decision for the owner. Cycle-4 scope tracked in task #84.
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
Three more cache-hit-path wins after PR #83 brought us from 1395 → 680ms.
1.
hashProjectPackageJsonrouted throughCache.hashFileWas reading pkg.json bytes + sha256ing on every task. Bypassed the
mtime+size fast path I added in PR #83. Fixed: every unchanged
pkg.json is now a stat + SQLite SELECT, not a file read.
2. Within-run memo of
hashProjectPackageJsonper projectDir100 projects × 3 tasks each previously read the same
package.json300 times per run. Now each projectDir resolves once; concurrent
tasks share the same in-flight
Promise<string>so there's no race.3. Within-run memo of
hashTaskConfigper config-object identityWeakMap<TaskConfig, sha256>. Config objects are created once atprepareRuntime; subsequenthashTaskConfig(cfg)calls hit theWeakMap, no JSON.stringify, no sha256.
Wiring
HashCache { packageJson, taskConfig }shape +createHashCache()factory in
src/orchestrator/execute-task.ts.prepareRun→PreparedRun.hashCache→ExecuteArgs.hashCache→
ComputeHashArgs.hashCache→ bothexecuteCachedTaskandplan().hashFileto theCacheLayerinterface (was only onCache).LayeredCache.hashFiledelegates to local.Tests
454 → 458 pass. New tests:
preserved across tasks
Cache.hashFile(verified by identitywith a direct re-call)
Expected impact
Per the previous bisect, pkg.json reads were ~200ms of the remaining
680ms. Combined with the WeakMap memos:
Gap to Turbo is now mostly per-task event-loop overhead. Closing
that needs a different design (batched per-task hash lookups + a
persistent task-hash cache).
https://claude.ai/code/session_016HXj6HW6bxSn8EYuKcxTD9
Generated by Claude Code