[9.x] Attribute Cast Performance Improvements #43554
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Performance improvements to casts-related functions:
getCasts()
in methodsstrncmp
withstr_starts_with
in cast type checkgetCastType()
to eliminate repeated string comparisons for cast types.Background:
I process millions of rows via scout and was curious if there were any obvious bottlenecks in the process to help speed things along. I discovered through profiling that casts-related code represented a significant number of overall calls and time spent processing my models.
In particular calls to
getCasts()
were unusually high as well and the time spent ingetCastType()
was higher than any other function.First step was to update any methods where
getCasts()
was called multiple times.getCastType()
seemed to be the primary culprit for the high number of calls togetCasts()
.When profiling again this helped reduce the calls to
getCasts()
but the time spent ingetCastType()
was still rather high. I foundisImmutableCustomDateTimeCast()
still was usingstrncmp
comparisons whenstr_starts_with
would do instead. This change is not only an improvement to readability while matching the code of the other similar functions, but there's also a small improvement in performance with the change as an added bonus.I then noticed that
getCastType()
can be cached as the results of checking the cast types are deterministic and do not need to be repeated for the same input.Results:
With these changes I saw a 5-25% reduction in processing time depending on the complexity, number of casts, and number of attributes of a particular model. From my tests I have noticed significant improvements on both eloquent load times and serialization.
Potential risks:
This introduces a new static member variable
$castTypeCache
toModel
that could cause conflicts if a subclass already defines one by that name. An alternative for this that is to introduce$castTypeCache
as a static variable to thegetCastType()
method only, which is often considered bad form but does not introduce any risk of naming collision and results in the same benefit.End user benefits:
General users will likely see negligible benefits as the changes are only really beneficial when processing a lot of models. At scale, any bulk loading and serializing of models will yield note-worthy performance improvements.
Other things to note about casts:
There are further significant performance improvements in caching
hasCast()
, however that is a bit more complex as there's no easy to trust that$casts
or the results ofgetCasts()
does not change during the lifecycle of a model. If a future version of Laravel introduced immutability to$casts
and an expectation of determinism forgetCasts()
the expensive and repeated calls tohasCast()
could be cached and save a significant amount of time.