Commit 4d84b54
committed
minor #3777 [LiveComponent] Cache the attribute-method lookups per component class (Kocal)
This PR was merged into the 3.x branch.
Discussion
----------
[LiveComponent] Cache the attribute-method lookups per component class
| 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, 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 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 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`:
```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);
}
```
Blackfire:
- before: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/8f8d948b-732a-44ca-a0a3-ebb9cbc48637/graph
- after: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/f0d0d013-2120-471b-8569-f0866a905d9e/graph
- diff: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/compare/8f8d948b-732a-44ca-a0a3-ebb9cbc48637...f0d0d013-2120-471b-8569-f0866a905d9e/graph
Analysis, implementation and benchmarks by Claude Opus 5.
Commits
-------
1d886b3 [LiveComponent] Cache the attribute-method lookups per component class1 file changed
Lines changed: 36 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
28 | 33 | | |
29 | 34 | | |
30 | 35 | | |
| |||
93 | 98 | | |
94 | 99 | | |
95 | 100 | | |
96 | | - | |
| 101 | + | |
97 | 102 | | |
98 | 103 | | |
99 | 104 | | |
| |||
111 | 116 | | |
112 | 117 | | |
113 | 118 | | |
114 | | - | |
| 119 | + | |
115 | 120 | | |
116 | 121 | | |
117 | 122 | | |
| |||
123 | 128 | | |
124 | 129 | | |
125 | 130 | | |
126 | | - | |
| 131 | + | |
127 | 132 | | |
128 | 133 | | |
129 | 134 | | |
| |||
135 | 140 | | |
136 | 141 | | |
137 | 142 | | |
138 | | - | |
| 143 | + | |
139 | 144 | | |
140 | 145 | | |
141 | 146 | | |
| |||
148 | 153 | | |
149 | 154 | | |
150 | 155 | | |
151 | | - | |
| 156 | + | |
152 | 157 | | |
153 | 158 | | |
154 | 159 | | |
155 | 160 | | |
156 | 161 | | |
157 | 162 | | |
158 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
159 | 190 | | |
0 commit comments