Skip to content

perf(cache): split runtime include map from transformation metadata - #594

Draft
lisachenko wants to merge 1 commit into
masterfrom
claude/goaop-i6-split-cache-state
Draft

perf(cache): split runtime include map from transformation metadata#594
lisachenko wants to merge 1 commit into
masterfrom
claude/goaop-i6-split-cache-state

Conversation

@lisachenko

Copy link
Copy Markdown
Member

What

AopComposerLoader materialized the full _transformation.cache array (filemtime + cacheUri per file) on every request, while the hot path only needs the originalPath => cachedPath mapping. The cache state is now two opcache-friendly PHP return-array files:

  • _include.cache — minimal originalPath => cacheUri|null map (null = known but untransformed), read by the autoloader on every request via the new CachePathManager::queryIncludeMap(); roughly half the data of the old array.
  • _transformation.cache — full build metadata, unchanged format, now loaded lazily and only on the cache-miss/weaving paths (queryCacheState() / flush). A hot request never materializes it.

Details:

  • flushCacheState() loads the full metadata before merging (so unqueried entries are never dropped) and writes both files; setCacheState() keeps the in-memory include map coherent.
  • Legacy cache dirs keep working: without _include.cache, the map is derived from the metadata once at boot (same cost as before the split) and the next flush/warmup writes both files.
  • findFile() semantics preserved exactly: production + known file → cached path (or original when untransformed, bypassing the filter); debug/unknown → filter path.

Measured

Paired back-to-back runs (fixture project, ~30 cached files, warm cache, prod, PHP 8.5, median of 21): init 1.30 → 1.18 ms, full request −0.18 ms (−5 %). The fixture's cache map is small — the cost this removes scales linearly with project size (a 10k-file app materializes a 10k-entry × 2-field array per request today; after this PR it's a flat string map, and the metadata array not at all).

Tests

New tests/Instrument/ClassLoading/CachePathManagerTest pins: both files written on flush, include map served without loading the full metadata (verified via the lazy-load flag), full metadata still lazily available, and the legacy-directory fallback. Full suite green on PHP 8.5/8.4 (2486 tests), PHPStan clean.

Part of the boot-time series

Fifth and final PR of the series (container laziness → lazy aspects → lazy transformers → trusted prebuilt cache → cache-state split).

🤖 Generated with Claude Code

https://claude.ai/code/session_01V1Z87HZ2iz23WPTtTYqFxE


Generated by Claude Code

The autoloader materialized the full _transformation.cache array (filemtime
plus cacheUri per file) on every request while only needing the
original-path => cached-path mapping. The cache state is now written as two
opcache-friendly files:

- _include.cache: minimal originalPath => cacheUri|null map, read by
  AopComposerLoader on every request (roughly half the data);
- _transformation.cache: full build metadata, now loaded lazily and only on
  the cache-miss/weaving paths (queryCacheState/flush) - a hot request never
  materializes it.

A legacy cache directory without _include.cache keeps working: the map is
derived from the metadata once and the next flush writes both files. New
CachePathManager::queryIncludeMap() serves the runtime map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1Z87HZ2iz23WPTtTYqFxE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants