This issue is automatically created based on existing pull request: #40614: perf(di:compile): memoize Interception Config scope reads during compilation
Description
Interception\Config\Config::generateIntercepted() is called twice during a single setup:di:compile run:
- The Interceptors generation phase (
Operation\Interception)
- The Interception cache generation phase (
Operation\InterceptionCache)
Both phases call $this->_reader->read($scope) for each of the ~8 compilation scopes. Each call fully parses and merges all di.xml files for that scope. The di.xml files don't change between the two calls within the same compile run, so the second set of reads is completely redundant.
On a project with 607 di.xml files across 8 scopes, that's ~4,856 file reads and DOM parse operations being done twice, with nearly 5,000 of them wasted.
Fix: add a $scopeReadCache array property to Config. On the first call for a given scope the result is stored; subsequent calls return the cached value. The cache is instance-scoped so it is automatically discarded between separate bin/magento invocations.
This is safe because:
di.xml files don't change during a compile run
- The
Config object is a DI singleton for the duration of the process
- There is no concurrency — both phases run sequentially in the same process
Related Pull Requests
Fixed Issues (if relevant)
Manual testing scenarios
Timing comparison
# Baseline on 2.4-develop
git checkout 2.4-develop
rm -rf generated/ var/cache/
time php bin/magento setup:di:compile --no-ansi 2>&1 | tee /tmp/compile-baseline.txt
# This branch
git checkout perf/di-compile-interception-scope-cache
rm -rf generated/ var/cache/
time php bin/magento setup:di:compile --no-ansi 2>&1 | tee /tmp/compile-patched.txt
To isolate the specific phase:
grep -i "interception cache" /tmp/compile-baseline.txt
grep -i "interception cache" /tmp/compile-patched.txt
On a project with ~400 modules the Interception cache generation phase typically drops from ~1.8–4.6s to ~0.6–1.7s (63–76% reduction).
Output correctness
cp -r generated/ /tmp/generated_patched
git checkout 2.4-develop && rm -rf generated/ var/cache/
php bin/magento setup:di:compile --no-ansi
diff -r /tmp/generated_patched/ generated/ # should produce no output
Benchmarks on real-world projects
| Project |
Interception cache phase before |
Interception cache phase after |
Saved |
| ~470 modules, 877 plugins |
1.8s |
0.6s |
1.2s / 67% |
| ~390 modules (Store1) |
4.1s |
1.0s |
3.1s / 76% |
| ~390 modules (Store2) |
4.6s |
1.7s |
2.9s / 63% |
Memory impact
Measured on a ~470-module install:
| Metric |
Baseline |
Patched |
Delta |
| PHP working memory at peak |
376 MB |
386 MB |
+10 MB (+2.7%) |
| OS peak RSS |
438 MB |
467 MB |
+29 MB (+6.7%) |
The ~29 MB increase is the cached scope data (8 scopes × ~3–4 MB each of parsed di.xml config) being retained across both phases instead of discarded and reloaded. For a CLI compile tool this is an acceptable trade-off and well within the typical memory_limit used for compilation.
Questions or comments
Happy to add integration-level coverage if needed. The existing unit test covers the cache behaviour directly.
Contribution checklist
This issue is automatically created based on existing pull request: #40614: perf(di:compile): memoize Interception Config scope reads during compilation
Description
Interception\Config\Config::generateIntercepted()is called twice during a singlesetup:di:compilerun:Operation\Interception)Operation\InterceptionCache)Both phases call
$this->_reader->read($scope)for each of the ~8 compilation scopes. Each call fully parses and merges alldi.xmlfiles for that scope. Thedi.xmlfiles don't change between the two calls within the same compile run, so the second set of reads is completely redundant.On a project with 607 di.xml files across 8 scopes, that's ~4,856 file reads and DOM parse operations being done twice, with nearly 5,000 of them wasted.
Fix: add a
$scopeReadCachearray property toConfig. On the first call for a given scope the result is stored; subsequent calls return the cached value. The cache is instance-scoped so it is automatically discarded between separatebin/magentoinvocations.This is safe because:
di.xmlfiles don't change during a compile runConfigobject is a DI singleton for the duration of the processRelated Pull Requests
Fixed Issues (if relevant)
Manual testing scenarios
Timing comparison
To isolate the specific phase:
On a project with ~400 modules the Interception cache generation phase typically drops from ~1.8–4.6s to ~0.6–1.7s (63–76% reduction).
Output correctness
Benchmarks on real-world projects
Memory impact
Measured on a ~470-module install:
The ~29 MB increase is the cached scope data (8 scopes × ~3–4 MB each of parsed di.xml config) being retained across both phases instead of discarded and reloaded. For a CLI compile tool this is an acceptable trade-off and well within the typical
memory_limitused for compilation.Questions or comments
Happy to add integration-level coverage if needed. The existing unit test covers the cache behaviour directly.
Contribution checklist