Skip to content

Check a type alias used by several keys once for a missing value type - #6643

Closed
Kocal wants to merge 1 commit into
phpstan:2.3.xfrom
Kocal:perf/missing-typehint-shared-alias
Closed

Kocal wants to merge 1 commit into
phpstan:2.3.xfrom
Kocal:perf/missing-typehint-shared-alias

Conversation

@Kocal

@Kocal Kocal commented Oct 1, 2026

Copy link
Copy Markdown

A type alias referenced by many keys of an array shape is one shared Type object. MissingTypehintCheck::getIterableTypesWithMissingValueTypehint() walked that object again for every key referring to it, and for every constant array it met it also ran a second full walk to drop its unsealed part.

The array shape that symfony/ux-css generates for its css() Twig function has about 840 keys, 126 of them referring to a nested shape, and checking one method parameter against it took seconds.

The callback only depends on the type it's given, so each object is now checked once. Later visits of the same object replay the descriptions added during the first visit, in the same order, so reported errors don't change, duplicates included. The inner walk that drops unsealed parts caches its result per object the same way. Objects are keyed with spl_object_id() and kept in the cache for the duration of the walk, so an id can't be reused in between.

Scenario Before After
New nested-array-shape-alias.php bench 117.4-117.9 ms 70.4-70.7 ms
Real ux-css shape, 2 levels, 1 call, turbo extension loaded 5.37-5.41 s 4.41-4.43 s

The rest of the phpbench suite stays within its existing thresholds. The two scenarios with the largest difference, bug-7140 and bug-8503, were re-run 3 times with 15 iterations each, and the difference turned out to be noise (22.40-22.57 ms vs 22.41-22.49 ms, and 123.9-125.6 ms vs 122.9-127.1 ms).

XDEBUG_MODE=off tests/vendor/bin/phpbench run --variant=nested-array-shape-alias.php --iterations=10 --report=aggregate

A type alias referenced by many keys of an array shape is one shared Type object. `MissingTypehintCheck::getIterableTypesWithMissingValueTypehint()` walked that object again for every key referring to it, and for every constant array it met it also ran a second full walk to drop its unsealed part. The array shape that symfony/ux-css generates for its `css()` Twig function has about 840 keys, 126 of them referring to a nested shape, and checking one method parameter against it took seconds.

The callback only depends on the type it's given, so each object is now checked once. Later visits of the same object replay the descriptions added during the first visit, in the same order, so reported errors don't change, duplicates included. The inner walk that drops unsealed parts caches its result per object the same way. Objects are keyed with `spl_object_id()` and kept in the cache for the duration of the walk, so an id can't be reused in between.

| Scenario | Before | After |
|---|---|---|
| New `nested-array-shape-alias.php` bench | 117.4-117.9 ms | 70.4-70.7 ms |
| Real ux-css shape, 2 levels, 1 call, turbo extension loaded | 5.37-5.41 s | 4.41-4.43 s |

The rest of the phpbench suite stays within its existing thresholds. The two scenarios with the largest difference, bug-7140 and bug-8503, were re-run 3 times with 15 iterations each, and the difference turned out to be noise (22.40-22.57 ms vs 22.41-22.49 ms, and 123.9-125.6 ms vs 122.9-127.1 ms).

```
XDEBUG_MODE=off tests/vendor/bin/phpbench run --variant=nested-array-shape-alias.php --iterations=10 --report=aggregate
```
@staabm

staabm commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Real ux-css shape .. Before 5.35s

how to reproduce this workload, so we can look into why this takes 5 seconds as a whole?

@Kocal

Kocal commented Oct 1, 2026

Copy link
Copy Markdown
Author

Real ux-css shape .. Before 5.35s

how to reproduce this workload, so we can look into why this takes 5 seconds as a whole?

Oh you're right, I'm creating a reproducer

@Kocal

Kocal commented Oct 1, 2026

Copy link
Copy Markdown
Author

@staabm

staabm commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

I took some profiles and put the information into a issue: phpstan/phpstan#15348

@staabm

staabm commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

@Kocal in my measures running the slow snippet on 2.2.16 will be waaaaay faster on 2.3.x, see phpstan/phpstan#15348

@Kocal

Kocal commented Oct 1, 2026

Copy link
Copy Markdown
Author

Ooooh awesome, thanks for investigating that much! :D

I saw that you opened some PRs, let me know if this one become useless

@staabm

staabm commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

yeah, I think we can close here. we found a more general approach which will impact even more use-case.

thanks for the nice reproducer, its really helpful.

@staabm staabm closed this Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants