Skip to content

Commit fb1a5c9

Browse files
committed
minor #50727 [DependencyInjection] Make better use of memory and CPU during auto-discovery (nicolas-grekas)
This PR was merged into the 6.4 branch. Discussion ---------- [DependencyInjection] Make better use of memory and CPU during auto-discovery | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - While profiling an app with ~2600 auto-discovered services, I figured out that using `unserialize(serialize())` for deep cloning was a significant memory and CPU hog. This patch skips deep-cloning when no definitions are nested in the prototype definition. From 328 MB → 239 MB (-27%) as measured by Blackfire.io From to 5.0s → 4.2s (-16%) (without Blackfire) :rocket: Commits ------- 8b0117983b [DependencyInjection] Make better use of memory and CPU during auto-discovery
2 parents 2a4e345 + 3c3c79e commit fb1a5c9

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

Loader/FileLoader.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,26 @@ public function registerClasses(Definition $prototype, string $namespace, string
119119
$autoconfigureAttributes = new RegisterAutoconfigureAttributesPass();
120120
$autoconfigureAttributes = $autoconfigureAttributes->accept($prototype) ? $autoconfigureAttributes : null;
121121
$classes = $this->findClasses($namespace, $resource, (array) $exclude, $autoconfigureAttributes, $source);
122-
// prepare for deep cloning
123-
$serializedPrototype = serialize($prototype);
122+
123+
$getPrototype = static fn () => clone $prototype;
124+
$serialized = serialize($prototype);
125+
126+
// avoid deep cloning if no definitions are nested
127+
if (strpos($serialized, 'O:48:"Symfony\Component\DependencyInjection\Definition"', 55)
128+
|| strpos($serialized, 'O:53:"Symfony\Component\DependencyInjection\ChildDefinition"', 55)
129+
) {
130+
// prepare for deep cloning
131+
foreach (['Arguments', 'Properties', 'MethodCalls', 'Configurator', 'Factory', 'Bindings'] as $key) {
132+
$serialized = serialize($prototype->{'get'.$key}());
133+
134+
if (strpos($serialized, 'O:48:"Symfony\Component\DependencyInjection\Definition"')
135+
|| strpos($serialized, 'O:53:"Symfony\Component\DependencyInjection\ChildDefinition"')
136+
) {
137+
$getPrototype = static fn () => $getPrototype()->{'set'.$key}(unserialize($serialized));
138+
}
139+
}
140+
}
141+
unset($serialized);
124142

125143
foreach ($classes as $class => $errorMessage) {
126144
if (null === $errorMessage && $autoconfigureAttributes) {
@@ -147,7 +165,7 @@ public function registerClasses(Definition $prototype, string $namespace, string
147165
if (interface_exists($class, false)) {
148166
$this->interfaces[] = $class;
149167
} else {
150-
$this->setDefinition($class, $definition = unserialize($serializedPrototype));
168+
$this->setDefinition($class, $definition = $getPrototype());
151169
if (null !== $errorMessage) {
152170
$definition->addError($errorMessage);
153171

0 commit comments

Comments
 (0)