Skip to content

Slow processing type hint of Class::* #7421

@neclimdul

Description

@neclimdul

Bug report

When updating an internal project from 1.3 to 1.7 phpstan ground to a halt. What used to take a second was taking minutes and on a constrained build server timing out after an hour just running phpstan. The later is extreme and maybe more a problem with the ci runner but I was suddenly blocked. I've generalized and open sourced the relevant parts of the project so it can be reviewed as part of this bug.

After a ton of profiling and bisecting and some red herrings the entire processing was on one line.
With the line:

./vendor/bin/phpstan  244.17s user 0.19s system 99% cpu 4:05.73 total

Without the line:

./vendor/bin/phpstan  1.15s user 0.12s system 92% cpu 1.376 total

What is this terrible line of code? This simple if statement: https://github.com/neclimdul/netsuite-search-iterator/blob/5714f4eac537930b9e55d7c7e1b82bd2d826bd00/src/Exception/StatusFailure.php#L70

The problem seems to be the type of that property. The relevant definition is
https://github.com/netsuitephp/netsuite-php/blob/3c2a5b19c1ab9879a8fc470076261359269098f9/src/Classes/StatusDetail.php#L21-L24
and the referenced types here https://github.com/netsuitephp/netsuite-php/blob/master/src/Classes/StatusDetailCodeType.php

There's something really weird going on here so I'm going to dump everything I've found.

  1. The profile of this line single line is massive. Without the line line the profile of phpstan running on the project is <1 G. With it is over 13 G.
  2. Removing the type from the property also fixes the performance.
  3. The profile signature is a bit hard to narrow down because if the size of the profile and how kcachegrind seems to process it but its clear the time is almost entirely spent in TypeCombinator::create -> TypeCombinator::remove -> TypeCombinator::intersect -> php::usort . There are 128 calls to ::remove but 499k calls to ::intersect. Kcachegrind and xdebug seem to be unhappy with the trace so some of the time numbers don't make sense but its clear almost all the time seems to be in ::itersect and usort.
  4. there is also a noticable amount of time spent in isSuperTypeOf calls from ::intersect with 998k calls.

Code snippet that reproduces the problem

https://github.com/neclimdul/netsuite-search-iterator/blob/5714f4eac537930b9e55d7c7e1b82bd2d826bd00/src/Exception/StatusFailure.php#L70

Expected output

Shorter scan of project.

Did PHPStan help you today? Did it make you happy in any way?

This was obviously frustrating taking a lot of time to narrow down and blocking development on this project but in the process of trying to narrow this down I tested it on a lot of vendor projects and found several issues that can be fixed. Also pushed me to finish open sourcing that library. There's always a silver lining if you look for it. 😄

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions