-
-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance optimization #387
base: master
Are you sure you want to change the base?
Conversation
…first - Reorder checks to prioritize definition lookup which is the most common case - Return true immediately if definition exists - Only check tag aliases if definition doesn't exist - Return false if neither exists Performance improvements: - benchPredefinedExisting: ~29% faster (130μs → 92μs) - benchUndefinedExisting: ~29% faster (123μs → 87μs) - benchUndefinedNonexistent: ~1% faster (446μs → 441μs) - Small improvements in other benchmarks
- Add fast path for existing instances - Extract StateResetter logic into separate method - Move StateResetter check to the end to avoid duplicate instance access - Improve code readability and maintainability Performance improvements: - benchSequentialLookups: ~3% faster (620μs → 599μs) - benchUndefinedExisting: ~5% faster (87μs → 83μs) - Other benchmarks show similar or slightly improved performance
- Add early returns for common simple cases (strings, ContainerInterface, Closure) - Simplify array definition handling - Improve code readability and reduce nesting Performance improvements: - benchConstruct: ~29% faster (336μs → 238μs) - benchSequentialLookups: ~13% faster (599μs → 523μs) - benchRandomLookups: ~16% faster (621μs → 524μs) - benchRandomLookupsComposite: ~9% faster (2.3ms → 2.1ms) - benchUndefinedNonexistent: ~5% faster (464μs → 440μs)
- Add normalized definitions cache with memory management - Reorder checks to prioritize most common cases - Add fast path for circular reference check - Improve code readability with better comments Performance improvements: - benchRandomLookupsComposite: ~4% faster (2.3ms → 2.2ms) - benchSequentialLookups: ~2% faster (544μs → 539μs) - benchRandomLookups: ~1.5% faster (546μs → 525μs) Memory usage is kept in check by clearing the cache when it reaches 100 entries.
- Simplify exception handling paths - Add fast path for NotFoundException with matching ID - Use ternary operators for cleaner code - Improve code readability with better comments Performance improvements: - benchRandomLookupsComposite: ~1% faster (2.2ms → 2.18ms) - benchSequentialLookups: ~0.5% faster (539μs → 536μs) - benchRandomLookups: ~0.5% faster (541μs → 538μs)
- Skip parsing if already a valid definition - Only validate meta if it's not empty - Only unset instance if it exists - Improve code readability with better comments Performance improvements: - benchConstruct: ~10% faster (238μs → 213μs) - benchSequentialLookups: ~3.5% faster (536μs → 517μs) - benchRandomLookups: ~3.7% faster (538μs → 518μs) - benchRandomLookupsComposite: ~0.5% faster (2.18ms → 2.17ms)
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #387 +/- ##
=============================================
- Coverage 100.00% 99.80% -0.20%
- Complexity 169 179 +10
=============================================
Files 11 11
Lines 492 505 +13
=============================================
+ Hits 492 504 +12
- Misses 0 1 +1 ☔ View full report in Codecov by Sentry. |
@@ -212,12 +239,16 @@ private function addDefinition(string $id, mixed $definition): void | |||
[$definition, $meta] = DefinitionParser::parse($definition); | |||
if ($this->validate) { | |||
$this->validateDefinition($definition, $id); | |||
$this->validateMeta($meta); | |||
// Only validate meta if it's not empty. | |||
if ($meta !== []) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not necessary as for me, foreach perfectly works with empty arrays
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call a method has cost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So why wouldn't you inline the whole framework in a single function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it makes sense here.
} | ||
// Fast path: check if instance exists. | ||
if (isset($this->instances[$id])) { | ||
return $id === StateResetter::class ? $this->prepareStateResetter($id) : $this->instances[$id]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't just a return $instances[$id]
?
prepareStateResetter
already holds values in the $instances
, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prepareStateResetter
checks delegates before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All delegates should be calculated and set one time, why they should do it every call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All delegates should be calculated and set one time, why they should do it every call?
I means check delegated containters. See current code:
if ($id === StateResetter::class) {
$delegatesResetter = null;
if ($this->delegates->has(StateResetter::class)) {
$delegatesResetter = $this->delegates->get(StateResetter::class);
}
* @param string $id | ||
* @return mixed | ||
*/ | ||
private function prepareStateResetter(string $id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private function prepareStateResetter(string $id) | |
private function prepareStateResetter(): StateResetter |
$id
is always StateResetter::class
throw new NotFoundException($id, [$id], previous: $exception); | ||
} | ||
// Fast path: check if instance exists. | ||
if (isset($this->instances[$id])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use array_key_exists
instead of isset
. Theoretically, result may be null
.
} | ||
throw new BuildingException($id, $e, $this->definitions->getBuildStack(), $e); | ||
} catch (Throwable $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems we should catch any exceptions from previous "catch" blocks also (for example, exceptions from delegates).
@@ -212,12 +239,16 @@ private function addDefinition(string $id, mixed $definition): void | |||
[$definition, $meta] = DefinitionParser::parse($definition); | |||
if ($this->validate) { | |||
$this->validateDefinition($definition, $id); | |||
$this->validateMeta($meta); | |||
// Only validate meta if it's not empty. | |||
if ($meta !== []) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call a method has cost.
array_map(static fn (array $data): mixed => $data[2], $methodsAndProperties), | ||
); | ||
// Skip validation for common simple cases. | ||
if (is_string($definition) || $definition instanceof ContainerInterface || $definition instanceof Closure) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should validate string definitions (on empty).
Validation process is not required hard performance optimization because he is not running in production environment.
if (!$this->definitions->has($id)) { | ||
throw new NotFoundException($id, $this->definitions->getBuildStack()); | ||
// Use cached normalized definition if available. | ||
if (!isset($this->normalizedDefinitions[$id])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What case when needs to normalize definition not once?
Before:
After: