Commit 48bed26
committed
[TwigComponent] Cache attribute-method reflection lookups per class
| Q | A
| -------------- | ---
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Documentation? | no
| Issues | -
| License | MIT
`AsTwigComponent::attributeMethodsFor()` rebuilt a `ReflectionClass` and
scanned every public method on each call. LiveComponent calls it five
times per component per request (`liveListeners()`, `preDehydrateMethods()`,
`postHydrateMethods()`, `preReRenderMethods()`, `isActionAllowed()`), so a
page with several live components re-ran the same reflection scan dozens
of times.
Memoize the resolved methods per class and attribute. The results only
depend on the class, so the cache is valid for the whole process.
`attributeMethodsFor()` returns the cached array directly and widens its
return type from `\Traversable` to `iterable`; both callers only iterate it.
20k lookups on a component with 13 public methods: ~35 ms -> ~5 ms.
Benchmarked through LiveComponent, where the callers live. LiveComponent
installs `symfony/ux-twig-component` from Packagist rather than from the
monorepo, so copy the patched file over
`src/LiveComponent/vendor/symfony/ux-twig-component/src/Attribute/AsTwigComponent.php`
first, then run from the repository root with
`blackfire run 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
{
public string $a = '';
public string $b = '';
public string $c = '';
#[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() {}
public function noise7() {}
public function noise8() {}
}
$component = new BenchComponent();
// Mimics 20 requests for a page holding 20 live components.
for ($i = 0; $i < 20 * 20; ++$i) {
AsLiveComponent::liveListeners($component);
AsLiveComponent::preDehydrateMethods($component);
AsLiveComponent::postHydrateMethods($component);
AsLiveComponent::preReRenderMethods($component);
AsLiveComponent::isActionAllowed($component, 'save');
}
```
Blackfire:
- before — 78ms wall / 69ms CPU: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/407f4e37-61aa-4dba-bacb-af56a3b2bde9/graph
- after — 20ms wall / 20ms CPU: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/c91ba592-76e1-4a08-a920-43bf67989efd/graph
- diff: https://app.blackfire.io/envs/5f4f9a62-eaa0-45ee-b7b3-a1b879f550e9/profiles/compare/407f4e37-61aa-4dba-bacb-af56a3b2bde9...c91ba592-76e1-4a08-a920-43bf67989efd/graph
Analysis, implementation and benchmarks by Claude Opus 5.1 parent 26a3746 commit 48bed26
1 file changed
Lines changed: 46 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
24 | 38 | | |
25 | 39 | | |
26 | 40 | | |
| |||
94 | 108 | | |
95 | 109 | | |
96 | 110 | | |
97 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
98 | 118 | | |
99 | 119 | | |
100 | 120 | | |
101 | 121 | | |
102 | 122 | | |
103 | | - | |
| 123 | + | |
104 | 124 | | |
105 | 125 | | |
106 | 126 | | |
107 | | - | |
| 127 | + | |
108 | 128 | | |
109 | 129 | | |
110 | 130 | | |
111 | | - | |
| 131 | + | |
112 | 132 | | |
113 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
114 | 152 | | |
115 | | - | |
| 153 | + | |
116 | 154 | | |
117 | 155 | | |
| 156 | + | |
| 157 | + | |
118 | 158 | | |
119 | 159 | | |
0 commit comments