Skip to content

Conversation

@mfn
Copy link
Collaborator

@mfn mfn commented Jan 3, 2022

Summary

#1289 calls getReturnType for every method on every model, which in
turn calls getReturnTypeFromDocBlock which has this code:

$phpDocContext = (new ContextFactory())->createFromReflector($reflection);

Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project before #1289 was introduced:

$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s

After #1289 :

$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s

However, in this case we do not need the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the actual type, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575

$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:

time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s

Fixes #1291

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • Misc. change (internal, infrastructure, maintenance, etc.)

Checklist

  • Existing tests have been adapted and/or new tests have been added
  • Add a CHANGELOG.md entry
  • Update the README.md
  • Code style has been fixed via composer fix-style

@mfn mfn self-assigned this Jan 3, 2022
@barryvdh
Copy link
Owner

barryvdh commented Jan 3, 2022

Nice :)

@mfn
Copy link
Collaborator Author

mfn commented Jan 3, 2022

Pinging also @SimonJnsson who brought us #1289 🎉

The difference can be seen in the test suite execution here too but I probably would not have noticed it:

barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After barryvdh#1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
@mfn mfn requested a review from barryvdh January 3, 2022 20:47
@barryvdh barryvdh merged commit f2a7f9d into barryvdh:master Jan 3, 2022
@SimonJnsson
Copy link
Contributor

@mfn thanks for the ping 👍

Good catch and good solution 👍 Glad someone noticed when i didn't 😅

@jonnywilliamson
Copy link

@mfn thanks so much for the quick analysis and fix!

Yes it's working just like before. Lovely and fast again!

======
Just one last (and more than likely naive) question. I assume it is a requirement that the function for get and set have a return type otherwise the docblock will not be updated?

Ie. If these two lines do NOT have a return type specifically specified, then nothing will be added to the docblock?

image

It's just because I assumed that if the function arguments had type hints then it could have been inferred and I spend 10 mins wondering why I wasn't getting anything until I noticed I didn't have a return type specified.

Thanks!

@SimonJnsson
Copy link
Contributor

@mfn thanks so much for the quick analysis and fix!

Yes it's working just like before. Lovely and fast again!

======
Just one last (and more than likely naive) question. I assume it is a requirement that the function for get and set have a return type otherwise the docblock will not be updated?

Ie. If these two lines do NOT have a return type specifically specified, then nothing will be added to the docblock?

image

It's just because I assumed that if the function arguments had type hints then it could have been inferred and I spend 10 mins wondering why I wasn't getting anything until I noticed I didn't have a return type specified.

Thanks!

Yep, we look at the return type of the get and set methods, thus if these are not provided no doc blocks will be created.

@mfn mfn deleted the mfn-speedup branch January 4, 2022 06:34
d3v2a pushed a commit to d3v2a/laravel-ide-helper that referenced this pull request Feb 16, 2024
…ryvdh#1292)

barryvdh#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ barryvdh#1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After barryvdh#1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
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.

Latest commit causes massive slowdown on writing to models

4 participants