bug #25474 [DI] Optimize Container::get() for perf (nicolas-grekas)
This PR was merged into the 3.4 branch.
Discussion
----------
[DI] Optimize Container::get() for perf
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #25159
| License | MIT
| Doc PR | -
As outlined in #25159, our beloved container suffers from a perf hit just because it has a second argument, and this second argument's default value makes it slower.
Let's fix this by inlining the value. This will put Symfony at a better rank on eg:
https://github.com/kocsismate/php-di-container-benchmarks
I benched also with the following script. The result is surprising (but matches the finding in #25159):
without the patch, it takes 2s, and with the patch, it's down to 1s (opcache enabled).
```php
require './vendor/autoload.php';
use Symfony\Component\DependencyInjection\Container;
$c = new Container();
$c->set('foo', new \stdClass());
$i = 10000000;
$s = microtime(1);
while (--$i) {
$c->get('foo');
}
echo microtime(true) - $s, "\n";
```
Commits
-------
4fe2551545
[DI] Optimize Container::get() for perf
This commit is contained in:
commit
020664e1ef
@ -264,7 +264,7 @@ class Container implements ResettableContainerInterface
|
||||
*
|
||||
* @see Reference
|
||||
*/
|
||||
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
|
||||
public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1)
|
||||
{
|
||||
// Attempt to retrieve the service by checking first aliases then
|
||||
// available services. Service IDs are case insensitive, however since
|
||||
@ -294,9 +294,9 @@ class Container implements ResettableContainerInterface
|
||||
|
||||
try {
|
||||
if (isset($this->fileMap[$id])) {
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
|
||||
return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
|
||||
} elseif (isset($this->methodMap[$id])) {
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
|
||||
return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
|
||||
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
|
||||
unset($this->loading[$id]);
|
||||
$id = $normalizedId;
|
||||
@ -306,7 +306,7 @@ class Container implements ResettableContainerInterface
|
||||
// and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
|
||||
@trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
|
||||
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$method}();
|
||||
return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$method}();
|
||||
}
|
||||
|
||||
break;
|
||||
@ -319,7 +319,7 @@ class Container implements ResettableContainerInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
|
||||
if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) {
|
||||
if (!$id) {
|
||||
throw new ServiceNotFoundException($id);
|
||||
}
|
||||
|
@ -1854,7 +1854,7 @@ EOF;
|
||||
} elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
|
||||
return 'null';
|
||||
} elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
|
||||
$code = sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
|
||||
$code = sprintf('$this->get(\'%s\', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $id, ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
} else {
|
||||
$code = sprintf('$this->get(\'%s\')', $id);
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ class ProjectServiceContainer extends Container
|
||||
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () {
|
||||
yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->getFoo_BazService()) && false ?: '_'};
|
||||
if ($this->has('invalid')) {
|
||||
yield 1 => ${($_ = isset($this->services['invalid']) ? $this->services['invalid'] : $this->get('invalid', ContainerInterface::NULL_ON_INVALID_REFERENCE)) && false ?: '_'};
|
||||
yield 1 => ${($_ = isset($this->services['invalid']) ? $this->services['invalid'] : $this->get('invalid', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'};
|
||||
}
|
||||
}, function () {
|
||||
return 1 + (int) ($this->has('invalid'));
|
||||
@ -302,12 +302,12 @@ class ProjectServiceContainer extends Container
|
||||
$this->services['method_call1'] = $instance = new \Bar\FooClass();
|
||||
|
||||
$instance->setBar(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'});
|
||||
$instance->setBar(${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->get('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE)) && false ?: '_'});
|
||||
$instance->setBar(${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->get('foo2', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'});
|
||||
if ($this->has('foo3')) {
|
||||
$instance->setBar(${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : $this->get('foo3', ContainerInterface::NULL_ON_INVALID_REFERENCE)) && false ?: '_'});
|
||||
$instance->setBar(${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : $this->get('foo3', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'});
|
||||
}
|
||||
if ($this->has('foobaz')) {
|
||||
$instance->setBar(${($_ = isset($this->services['foobaz']) ? $this->services['foobaz'] : $this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE)) && false ?: '_'});
|
||||
$instance->setBar(${($_ = isset($this->services['foobaz']) ? $this->services['foobaz'] : $this->get('foobaz', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)) && false ?: '_'});
|
||||
}
|
||||
$instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default"))));
|
||||
|
||||
|
Reference in New Issue
Block a user