Skip to content

perf(container): several improvements - #2319

Draft
brendt wants to merge 3 commits into
3.xfrom
perf-container
Draft

brendt wants to merge 3 commits into
3.xfrom
perf-container

Conversation

@brendt

@brendt brendt commented Oct 23, 2026

Copy link
Copy Markdown
Member

No description provided.

Every class without a registered definition was checked against all dynamic initializers, which were rebuilt after every container reset. In worker mode, this amounted to more than 400 `canInitialize()` calls per request (#2309).

The container now remembers which initializer, if any, can initialize a class and tag. This is kept across resets, since it only depends on the registered initializers, and is forgotten when initializers are added or removed. Resolved initializers are now also reused until the container is reset, instead of being autowired every time they're used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brendt
brendt requested a review from innocenzi as a code owner October 23, 2026 11:45
Autowiring a class reflected on it from scratch every time: its constructor, parameters, properties, and the `#[Tag]`, `#[Proxy]`, `#[Inject]` and `#[Singleton]` attributes were all looked up and instantiated again. Since class definitions can't change at runtime, this data is now cached once and shared between containers.

Resolving an already resolved singleton also no longer reflects on its class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Benchmark Results

Comparison of perf-container against 3.x (1509573a2148600c284435dde8761638d30aa2b9).

Open to see the benchmark results
Benchmark Set Mem. Peak Time Variability
GenericRouterBench(benchDispatch) Static route 5.285mb +0.42% 33.815μs -35.12% ±0.67% +66.62%
GenericRouterBench(benchDispatch) Dynamic route 5.289mb +0.43% 47.546μs -28.11% ±0.87% -5.53%
GenericRouterBench(benchDispatch) Dynamic short 5.289mb +0.43% 46.906μs -28.96% ±0.64% -31.59%
GenericRouterBench(benchDispatchWithoutMiddleware) Static route 5.284mb +0.42% 29.425μs -33.05% ±0.92% +48.07%
GenericRouterBench(benchDispatchWithoutMiddleware) Dynamic route 5.288mb +0.43% 42.793μs -26.81% ±0.83% -5.10%
GenericRouterBench(benchDispatchWithoutMiddleware) Dynamic short 5.288mb +0.43% 42.208μs -26.63% ±1.44% +129.72%
ContainerBench(benchAutowireSimple) - 4.095mb +0.66% 1.592μs -63.20% ±0.71% -17.31%
ContainerBench(benchAutowireNested) - 4.172mb +0.50% 7.960μs -48.16% ±1.25% +125.77%
ContainerBench(benchResolveSingletonInstance) - 4.093mb +0.72% 0.712μs -26.04% ±1.93% +275.42%
ContainerBench(benchSingletonAttribute) - 4.141mb +0.46% 0.704μs -27.82% ±1.84% +196.13%
ContainerBench(benchResolveWithInitializer) - 4.135mb +0.48% 2.592μs -60.86% ±0.87% -44.94%
ContainerBench(benchResolveWithDynamicInitializer) - 4.139mb +0.47% 2.725μs -42.70% ±0.92% +3.90%
ContainerBench(benchResolveClosureSingleton) - 4.093mb +0.72% 0.718μs -26.28% ±1.14% +24.33%
ContainerBench(benchInvokeClosure) - 4.146mb +0.41% 8.505μs -22.55% ±0.68% +7.49%
ObjectHydrationBench(benchMapWithColdMapperCache) - 6.771mb +1.32% 122.313μs -11.36% ±0.35% -31.81%

Generated by phpbench against commit 1199111

@brendt
brendt marked this pull request as draft October 23, 2026 11:54
Remembering initializer lookups added two objects to every container, which made constructing a container slower and increased memory usage, as shown by the register benchmarks. They are now created once they're first needed, or right before the container is cloned so that clones still share them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant