-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Serializer] Improve performance by exposing supports-never/-always #45779
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
Conversation
src/Symfony/Component/Serializer/Normalizer/CacheableSupportsInterface.php
Outdated
Show resolved
Hide resolved
Hey! I think @mtarld has recently worked with this code. Maybe they can help review this? Cheers! Carsonbot |
Is release 6.1 feature-locked yet, or could this still be considered for it ? |
@dunglas Github has autosuggested you as reviewer. Do you think that's correct or would there be a better fit that I could contact for a review? Thanks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nitpicking, otherwise LGTM.
src/Symfony/Component/Serializer/Normalizer/CacheableSupportsInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Serializer/Normalizer/CacheableSupportsInterface.php
Outdated
Show resolved
Hide resolved
Can you rebase and add a changelog entry? |
Actually I'd prefer to let us more time to review this change as I'm not fond of the proposed API. |
@chalasr Alright, do you have any suggestion for the implementation ? |
@Jeroeny not yet, we're focus on stabilizing 6.1 right now. But I'm confident giving us some more time will allow us to explore other possibilities, even if we stick to this one in the end. |
Rebased and targeting |
src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php
Show resolved
Hide resolved
src/Symfony/Component/Serializer/Normalizer/CacheableSupport.php
Outdated
Show resolved
Hide resolved
return $normalizer; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private function supportsNormalizationWrapper(NormalizerInterface $normalizer, mixed $data, ?string $format, array $context): CacheableSupport |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can find a more meaningful name for that private method?
Or at least add a comment telling that is a BC layer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment. The name could be better indeed, but haven't thought of anything better yet, am open to suggestions.
$this->assertFalse($serializer->supportsNormalization(new \StdClass(), 'json', ['TEST_CONTEXT' => true])); | ||
$this->assertFalse($serializer->supportsNormalization(new \StdClass(), 'json', ['TEST_CONTEXT' => true])); | ||
$this->assertFalse($serializer->supportsNormalization(new \StdClass(), 'json', ['TEST_CONTEXT' => true])); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can test the SupportNever
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is testing that, by calling supports..
multiple times and ensuring supportsNormalization
is only called 3 times instead of 5.
$normalizer
->expects($this->exactly(3))
->method('supportsNormalization')
Unless you'd say something more explicit could be added?
694b252
to
0fb7743
Compare
…ate supports-never, -always
Thanks for giving this a try. I think the approach in #49291 might provide better results. |
Closing in favor #49291, thanks again. |
…better performance (tucksaun, nicolas-grekas) This PR was merged into the 6.3 branch. Discussion ---------- [Serializer] Add methods `getSupportedTypes` to allow better performance | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | yes (new method in the interfaces, one interface deprecated) | License | MIT | Doc PR | to be written The PRs allows normalizers or denormalizers to expose their supported types and the associated cacheability to the Serializer. With this info, even if the `supports*` call is not cacheable, the Serializer can skip a ton of method calls to `supports*` improving performance substaintially in some cases:  <details> <summary>I found this design while working on a customer project performance (a big app built around API Platform): we reached the point where the slowest part of main application endpoint was `Symfony\Component\Serializer\Serializer::getNormalizer`.</summary> After some digging, we found out we were experiencing the conjunction of two phenomenons: - the application is quite complex and returns deep nested and repeating structures, exposing the underlying bottleneck; - and a lot of custom non-cacheable normalizers. Because most of the normalizers are not cacheable, the Serializer has to call every normalizer over and over again leading `getNormalizer` to account for 20% of the total wall time:  We first tried to improve cacheability based on context without much success, then an approach similar to #45779 with some success but still feeling this could be faster. We then thought that even if the `supportsNormalization` could not be cached (because of the context), maybe we could avoid the calls at the origin by letting the `Normalizers` expose the types they support and came to this PR with pretty good results. </details> The perfornance improvement was only measured by adapting Symfony's normalizers as well as the project ones, proper third party normalizers updates should improve performance even more. This should effectively replaces the `CacheableSupportsMethodInterface` as the cacheability can now be returned by `getSupportedTypes`. Commits ------- e5af24a [Serializer] Add wildcard support to getSupportedTypes() 400685a [Serializer] Add methods `getSupportedTypes` to allow better performance
Description
In the Serializer component, there is a
CacheableSupportsMethodInterface
. It can indicate the fact the normalizer'ssupports*
method result can be cached.Unfortunately, every normalizer that determines this result using
$context
cannot have its result cached (because it's based onformat
+type
).However there is some information with that supports call that often can be cached. Which is when a normalizer never supports a
format
+type
, regardless of context.For example:
This normalizer would not be able to have its result cached. Even though we know that if the
$data
is not an instance ofDateTime
, it will never be supported, regardless of$context
.Solution(s)
This pull request proposes to introduce the ability to return
always/never supports $format+$type
, to increase performance.By introducing or altering a method to return one of 4 values:
There are multiple ways to achieve this.
supportsNormalization
method.It would be changed to allow it to return one of the enum values (e.g. using integers -1 to 2, or bitwise), while still allowing
bool
return for backwards compatibilty.This has the advantage of only needing one method for the caching and supports information. And no extra interfaces, so
CacheableSupportsMethodInterface
can be deprecated.With this, the example from earlier would become:
CacheableSupportsInterface::supportsNormalizationCached($data, $format, $context): int
. Possibly with a trait that implements thesupportsNormalization
to prevent duplicate logic (by doingreturn $this->supportsNormalizationCached(..) >= SUPPORT
).The solutions apply to both normalization and denormalization.
Performance impact example
CacheableSupportsMethodInterface
because of$context
, but can returnSUPPORT_NEVER
like the example above.DateTime
type.Now you normalize a
$dateTime
1000 times:supportsNormalization()
and iterated over 10.010$cached
normalizers .supportsNormalization()
and iterated over 1.010$cached
normalizers ($this->normalizerCache[$format][DateTime::class]
now only contains the one relevant normalizer).A decrease in method calls of 89.91%.