[LiveComponent] Cache the attribute-method lookups per component class - #3777
Merged
Kocal merged 1 commit intoAug 20, 2026
Merged
Conversation
Member
// Mimics 20 requests for a page holding 20 live components.
for ($i = 0; $i < 20 * 20; ++$i) {Not a very realistic mimic :) Assuming 20 independent LiveComponents rendered once on the initial page:
And I'm not sure to get the "n requests in parralel" is doing here. But if there are some gain for 2/5 of them, why not 👍 Now, i'm not very happy we re-contaminate TwigComponent with code made for LiveComponent, and i'd rather we have these methods in the AsLiveComponent attribute directly if possible.. |
Kocal
force-pushed
the
perf/twig-component-attribute-methods-cache
branch
5 times, most recently
from
August 16, 2026 12:23
48bed26 to
3297487
Compare
Member
Author
|
Benchmarks updated with a more real-world usage, now only LiveComponent is modified |
| Q | A
| -------------- | ---
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Documentation? | no
| Issues | -
| License | MIT
`liveListeners()`, `preDehydrateMethods()`, `postHydrateMethods()`,
`preReRenderMethods()` and `isActionAllowed()` each rebuild a
`ReflectionClass` and walk every public method of the component, on every
render and every request. What they return only depends on the component
class.
Memoize the resolved methods per class and attribute. An earlier revision of
this branch put the cache in `AsTwigComponent`, where those helpers live, but
they have no other consumer than `AsLiveComponent`, so the cache belongs here
and TwigComponent is left untouched.
Timings use the call counts from the review: a page holding five
LiveComponents calls `liveListeners()` and `preDehydrateMethods()` once per
component on the initial render, and a single component update calls each of
the five once.
initial render, 5 components 18.1 us -> 2.9 us
one component update 8.2 us -> 1.0 us
Sixteen microseconds off a page render is not going to show up in a profile;
the ratio is what it is because reflection is simply not needed more than
once per class.
Benchmarked from the repository root with `symfony php bench.php`:
```php
<?php
require __DIR__.'/src/LiveComponent/vendor/autoload.php';
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\PostHydrate;
use Symfony\UX\LiveComponent\Attribute\PreDehydrate;
use Symfony\UX\LiveComponent\Attribute\PreReRender;
#[AsLiveComponent('Bench')]
class BenchComponent
{
#[PreDehydrate] public function onPreDehydrate() {}
#[PostHydrate] public function onPostHydrate() {}
#[PreReRender] public function onPreReRender() {}
#[LiveAction] public function save() {}
#[LiveListener('foo:bar')] public function onFooBar() {}
public function noise1() {} public function noise2() {} public function noise3() {}
public function noise4() {} public function noise5() {} public function noise6() {}
}
$component = new BenchComponent();
$render = static function () use ($component) {
for ($i = 0; $i < 5; ++$i) {
AsLiveComponent::liveListeners($component);
AsLiveComponent::preDehydrateMethods($component);
}
};
$update = static function () use ($component) {
AsLiveComponent::liveListeners($component);
AsLiveComponent::preDehydrateMethods($component);
AsLiveComponent::postHydrateMethods($component);
AsLiveComponent::preReRenderMethods($component);
AsLiveComponent::isActionAllowed($component, 'save');
};
foreach (['initial render' => $render, 'one update' => $update] as $label => $fn) {
$fn();
$start = hrtime(true);
for ($i = 0; $i < 2000; ++$i) { $fn(); }
printf("%-16s %6.1f us\n", $label, (hrtime(true) - $start) / 1e3 / 2000);
}
```
Analysis, implementation and benchmarks by Claude Opus 5.
Kocal
force-pushed
the
perf/twig-component-attribute-methods-cache
branch
from
August 20, 2026 07:07
3297487 to
1d886b3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
liveListeners(),preDehydrateMethods(),postHydrateMethods(),preReRenderMethods()andisActionAllowed()each rebuild aReflectionClassand walk every public method of the component, on every render and every request, even though what they return only depends on the component class.Memoize the resolved methods per class and attribute. An earlier revision of this branch put the cache in
AsTwigComponent, where those helpers live, but they have no other consumer thanAsLiveComponent, so the cache belongs here andTwigComponentis left untouched.Timings use the call counts from the review: a page holding five LiveComponents calls
liveListeners()andpreDehydrateMethods()once per component on the initial render, and a single component update calls each of the five once.Sixteen microseconds off a page render isn't going to show up in a profile; the ratio is what it is because reflection simply isn't needed more than once per class.
Benchmarked from the repository root with
symfony php bench.php:Blackfire:
Analysis, implementation and benchmarks by Claude Opus 5.