Skip to content
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

Implement OversizedArrayVisitor to improve huge constant array performance #2116

Merged
merged 1 commit into from Dec 20, 2022

Conversation

staabm
Copy link
Contributor

@staabm staabm commented Dec 15, 2022

implements the idea described in phpstan/phpstan#8215 (comment)

closes phpstan/phpstan#8215

with this fix, the snippet is analyzable in 2 seconds, while it takes several minutes before.

@staabm
Copy link
Contributor Author

staabm commented Dec 15, 2022

it needs a few fixes for existing tests, but what do you think about the approach in general?

@ondrejmirtes
Copy link
Member

Can you explain your reasoning behind this code? I don't entirely understand it.

@staabm
Copy link
Contributor Author

staabm commented Dec 16, 2022

the idea is, that phpstan is slow when analyzing huge arrays, like gist because it tries to get the most precise result.

we already have an OversizedArrayType, but it works in a way that it needs to build up the constant array using ConstantTypes and then collapsing it into a OversizedArrayType, after realizing its too big. in huge cases even the steps to build up the Constant types and collapse them into OversizedArrayType already takes minutes.

this PR tries to add a very lightweight, but imprecise type inference when very big constant arrays are analyzed.
we determine a rough shape based on only the simplest types, to make sure we don't later run into the very slow Constant types -> OversizedArrayType case.

see the blackfire profile of said gist, where we are super slow because of the sheer number of constant-integer types invovled

grafik

@ondrejmirtes
Copy link
Member

  1. Does this also make https://github.com/phpstan/phpstan-src/blob/aa7b1eabd197ef6b7fb2c8eebbb7876aebc0ba6a/tests/PHPStan/Analyser/data/bug-8146.php fast?
  2. Does this make https://github.com/phpstan/phpstan-src/blob/98c66f30dea8bbb075e327cb5c415427000446e8/tests/PHPStan/Analyser/data/bug-8146a.php fast?
  3. Does this get rid of false positives when an array type is simplified but the array is returned from a method that typehints a precise array shape?

I need at least 1) and 3) to be true. I understand that 2) might be a different problem.

@staabm
Copy link
Contributor Author

staabm commented Dec 16, 2022

  1. Does this also make aa7b1ea/tests/PHPStan/Analyser/data/bug-8146.php fast?

runs locally in 13 seconds. on the latest 1.9.x-dev it runs in 1min 18 secs.

2. Does this make 98c66f3/tests/PHPStan/Analyser/data/bug-8146a.php fast?

I don't think this counts as a constant array in a sense this Visitor could improve. analyzing this source requires a already built up scope, or at least knowledge about the returned types of methods.

3. Does this get rid of false positives when an array type is simplified but the array is returned from a method that typehints a precise array shape?

I will work on this one now

@staabm staabm force-pushed the oversized-array branch 3 times, most recently from ef9f751 to c3955a3 Compare December 16, 2022 11:26
@@ -1080,31 +1076,7 @@ private function specifyTypesForConstantStringBinaryExpression(
&& strtolower($exprNode->name->toString()) === 'gettype'
&& isset($exprNode->getArgs()[0])
) {
$type = null;
if ($constantType->getValue() === 'string') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extracted into new helper for re-use in new visitor

@staabm staabm force-pushed the oversized-array branch 2 times, most recently from 04ea982 to a732639 Compare December 16, 2022 11:33
@staabm staabm marked this pull request as ready for review December 16, 2022 11:33
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm staabm force-pushed the oversized-array branch 2 times, most recently from 19f2442 to b33c3cc Compare December 16, 2022 11:41
@staabm staabm marked this pull request as draft December 16, 2022 15:29
@staabm staabm marked this pull request as ready for review December 18, 2022 09:46
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm
Copy link
Contributor Author

staabm commented Dec 18, 2022

Backward Compatibility

fails because I have removed a @deprecated annotation - I guess.
I think its a false positive

@staabm
Copy link
Contributor Author

staabm commented Dec 18, 2022

phpstan/phpstan#8215 sees a ~25x improvement

➜  phpstan-src git:(oversized-array) ✗ php bin/phpstan analyze tests/PHPStan/Analyser/data/bug-8146b.php --debug -vvv
Note: Using configuration file /Users/staabm/workspace/phpstan-src/phpstan.neon.dist.
Result cache not used because of debug mode.
    0 [▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░] < 1 sec 24.0 MiB

Result cache was not saved because only files were passed as analysed paths.
 [OK] No errors

Used memory: 74 MB
➜  phpstan-src git:(oversized-array) ✗ php bin/phpstan analyze test.php --debug -vvv
Note: Using configuration file /Users/staabm/workspace/phpstan-src/phpstan.neon.dist.
Result cache not used because of debug mode.
/Users/staabm/workspace/phpstan-src/test.php
--- consumed 91.98 MB, total 142 MB, took 23.03 s
Result cache was not saved because only files were passed as analysed paths.
 [OK] No errors

Used memory: 142 MB

phpstan/phpstan#8146 (comment) sees a 2x improvement

➜  phpstan-src git:(oversized-array) ✗ php bin/phpstan analyze test.php --debug -vvv
Note: Using configuration file /Users/staabm/workspace/phpstan-src/phpstan.neon.dist.
Result cache not used because of debug mode.
/Users/staabm/workspace/phpstan-src/test.php
--- consumed 109.98 MB, total 160 MB, took 21.31 s
Result cache was not saved because only files were passed as analysed paths.

➜  phpstan-src git:(1.9.x) ✗ php bin/phpstan analyze test.php --debug -vvv
Note: Using configuration file /Users/staabm/workspace/phpstan-src/phpstan.neon.dist.
Result cache not used because of debug mode.
/Users/staabm/workspace/phpstan-src/test.php
--- consumed 136 MB, total 160 MB, took 42.41 s

@ondrejmirtes
Copy link
Member

I don't get what's exactly happening in the visitor, it's a lot of code.

And I don't think it has to be a visitor, to me it looks like it's essentially caching for something that could be in InitializerExprTypeResolver where Array_ type is resolved.

@staabm staabm marked this pull request as draft December 18, 2022 15:50
@staabm
Copy link
Contributor Author

staabm commented Dec 18, 2022

ohh thats a good point. let me see whether I can get rid of the Visitor.

@staabm
Copy link
Contributor Author

staabm commented Dec 18, 2022

great suggestion, it got even simpler now.

@staabm staabm marked this pull request as ready for review December 18, 2022 16:23
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

$isList = false;

$itemKeyType = $getTypeCallback($key);
if (!$itemKeyType instanceof ConstantScalarType || $key instanceof ClassConstFetch) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We skip const class fetch, as I was not able to make ArrayType accept a OversizedArrayType with reduced precision when using Constant enumerations like self::SOME_*.
See failing test when removing this check

}

$itemValueType = $getTypeCallback($value);
if (!$itemValueType instanceof ConstantScalarType || $value instanceof ClassConstFetch) {
Copy link
Contributor Author

@staabm staabm Dec 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could also add support for arrays where const arrays call methods/functions as long as the return type is defined.

example:

public function getData(): array
 	{
 		return [
 			'Bács-Kiskun' => [
 				'Ágasegyháza' => [
 					'constituencies' => ['Bács-Kiskun 4.'],
 					'coordinates' => ['lat' => 46.8386043, 'lng' => 19.4502899],
 				],
 			         $this->returnsString() => [
 					'constituencies' => ['Bács-Kiskun 3.'],
 					'coordinates' => ['lat' => 46.6898175, 'lng' => 19.205086],
 				],
 				'Apostag' => [
 					'constituencies' => ['Bács-Kiskun 3.'],
 					'coordinates' => ['lat' => 46.8812652, 'lng' => $this->returnsFloat()],
 				],

I did not yet work on it, as I think we are still discussing whether the change itself is acceptable.

@ondrejmirtes
Copy link
Member

I just pushed a simplification of the buildDegradedConstantArray method as I imagine it should work.

A few problems with it though - some tests fail. I think that we'll have to modify not only accepts, but isSuperTypeOf as well.

Also - it's not always correct. When $key === null needs to be handled, and also $isList isn't always truthful.

@staabm staabm marked this pull request as draft December 19, 2022 12:07
@ondrejmirtes
Copy link
Member

Hi, I'm taking over this PR and I'll try to finish it. Thanks.

@staabm
Copy link
Contributor Author

staabm commented Dec 20, 2022

I tried several things which didn't work. Thanks for taking it

@ondrejmirtes ondrejmirtes force-pushed the oversized-array branch 2 times, most recently from 9ebf4a4 to 1573a79 Compare December 20, 2022 11:00
@@ -809,4 +809,15 @@ public function testBug8223(): void
]);
}

public function testBug8146b(): void
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I misstyped the testname

Suggested change
public function testBug8146b(): void
public function testBug8146c(): void

@ondrejmirtes ondrejmirtes force-pushed the oversized-array branch 2 times, most recently from 248dd35 to 7d40377 Compare December 20, 2022 14:34
@ondrejmirtes ondrejmirtes marked this pull request as ready for review December 20, 2022 21:21
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@ondrejmirtes ondrejmirtes merged commit ae37fb9 into phpstan:1.9.x Dec 20, 2022
@ondrejmirtes
Copy link
Member

Thank you.

@staabm
Copy link
Contributor Author

staabm commented Dec 20, 2022

Thank you 🙏

@ddebin
Copy link

ddebin commented Jan 3, 2023

Do you think we can disable this new behavior? Now, my huge const is oversimplified and the array shape is not guessed as precisely as before.

@ondrejmirtes
Copy link
Member

@ddebin Please open a new issue and make sure to include a code sample.

@ddebin
Copy link

ddebin commented Jan 3, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants