Skip to content

[Issue] perf(di:compile): avoid PluginList clone per area — 70% reduction in Interceptors generation phase #40850

@m2-assistant

Description

@m2-assistant

This issue is automatically created based on existing pull request: #40613: perf(di:compile): avoid PluginList clone per area — 70% reduction in Interceptors generation phase


Description

InterceptionConfigurationBuilder::getPluginsList() clones $this->pluginList once per compilation area (8 areas by default: global, frontend, adminhtml, crontab, webapi_rest, webapi_soap, graphql, admin).

Each clone does two things in sequence:

  1. Deep-copies the entire PluginList object graph — _data, _inherited, _processed, _pluginInstances, _scopePriorityScheme and all merged plugin config.
  2. Immediately calls _loadScopedData(), which re-merges plugin config from scratch into the clone, overwriting everything that was just copied.

The clone allocates and copies a large object graph, then throws all the copied data away. With ~877 plugins across 8 areas this creates significant unnecessary allocation and GC pressure in the Interceptors generation phase.

Fix: add a reset() method to the PluginList subclass that clears those five properties back to their initial values. Replace clone $this->pluginList in InterceptionConfigurationBuilder with $this->pluginList->reset(), reusing the same instance across areas.

All five reset properties are ones that _loadScopedData() fully rebuilds anyway, so the result is identical to the clone approach. _scopePriorityScheme is reset to [Area::AREA_GLOBAL], matching the value set in the parent constructor and the correct starting state before _loadScopedData() runs.

This is safe because areas are processed sequentially within a single request — there is no shared mutable state across concurrent callers.

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/
time php bin/magento setup:di:compile --no-ansi 2>&1 | tee /tmp/compile-baseline.txt

# This branch
git checkout perf/di-compile-pluginlist-reset
rm -rf generated/
time php bin/magento setup:di:compile --no-ansi 2>&1 | tee /tmp/compile-patched.txt

# Compare the Interceptors generation phase specifically
grep -i "interceptor" /tmp/compile-baseline.txt
grep -i "interceptor" /tmp/compile-patched.txt

On a project with ~400 modules expect the Interceptors phase to drop from roughly 14–15s down to 3–5s, and total compile time to improve by around 35–40%.

Output correctness

# Keep generated/ from the branch run above, then compare against 2.4-develop output
cp -r generated/ /tmp/generated_patched
git checkout 2.4-develop && rm -rf generated/
php bin/magento setup:di:compile --no-ansi
diff -r /tmp/generated_patched/ generated/   # should produce no output

Benchmarks on real-world projects

Project Interceptors before Interceptors after Saved
~470 modules, 877 plugins 8.8s 2.2s 6.6s / 75%
~390 modules (Store1) 15.5s 3.4s 12.1s / 78%
~390 modules (Store2) 14.1s 4.3s 9.8s / 69%

Questions or comments

Happy to add integration-level coverage for the InterceptionConfigurationBuilder flow if that would help reviewers. The existing unit test already covers the reset() method and the call site change.

Contribution checklist

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • README.md files for modified modules are updated and included in the pull request if any predefined sections require an update — no README changes needed for this fix
  • All automated tests passed successfully (all builds are green)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Ready for Development

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions