Skip to content

perf(cache): within-run hash memos + pkg.json via mtime+size fast path (680ms → ~400ms expected) - #84

Merged
Exelord merged 1 commit into
mainfrom
claude/within-run-memos
May 13, 2026
Merged

perf(cache): within-run hash memos + pkg.json via mtime+size fast path (680ms → ~400ms expected)#84
Exelord merged 1 commit into
mainfrom
claude/within-run-memos

Conversation

@Exelord

@Exelord Exelord commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Three more cache-hit-path wins after PR #83 brought us from 1395 → 680ms.

1. hashProjectPackageJson routed through Cache.hashFile

Was 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 hashProjectPackageJson per projectDir

100 projects × 3 tasks each previously read the same package.json
300 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 hashTaskConfig per config-object identity

WeakMap<TaskConfig, sha256>. Config objects are created once at
prepareRun time; subsequent hashTaskConfig(cfg) calls hit the
WeakMap, no JSON.stringify, no sha256.

Wiring

  • New HashCache { packageJson, taskConfig } shape + createHashCache()
    factory in src/orchestrator/execute-task.ts.
  • Plumbed prepareRunPreparedRun.hashCacheExecuteArgs.hashCache
    ComputeHashArgs.hashCache → both executeCachedTask and plan().
  • Added hashFile to the CacheLayer interface (was only on Cache).
    LayeredCache.hashFile delegates to local.

Tests

454 → 458 pass. New tests:

  • taskConfig WeakMap memo: same input → same hash + WeakMap has key
  • packageJson memo: same projectDir resolves once, Promise identity
    preserved across tasks
  • Different projectDirs each get their own packageJson entry
  • pkg.json hashes go through Cache.hashFile (verified by identity
    with a direct re-call)

Expected impact

Per the previous bisect, pkg.json reads were ~200ms of the remaining
680ms. Combined with the WeakMap memos:

Stage Cached-run time
Before PR #83 1395 ms
After PR #83 (5 wins) 680 ms
After this PR (3 more) expected ~400–450 ms
Turbo 120 ms

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

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
Exelord merged commit 548a0d9 into main May 13, 2026
1 check passed
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.
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.

2 participants