Ruby: Improve performance of flow through (hash) splats #14229
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.
This PR builds on top of #14090. This DCA run shows the combined effect of those two PRs, while this DCA run is for this PR alone.
Hash splats
We support flow through hash-splats in all possible combinations:
The first case (
m1
) is handled by synthesizing an implicit hash-splat parameterimplicit-hash-param
insidem1
, and then adding read steps out ofimplicit-hash-param
intokey1
(resp.key2
), reading out aKnownElementContent
corresponding to the symbol:key1
(resp.:key2
):The second case is handled dually: we synthesize an implicit hash-splat argument
implicit-hash-arg
at the call site, and add store steps from the keyword arguments intoimplicit-hash-arg
:The third case is handled using a combination, but care must be taken to ensure that the
:key1
argument does not end up in**kwargs
(we do this usingclearsContent
).Reusing
KnownElementContent
for the implicit stores/reads makes semantically sense. However, performance-wise it is not great, as we are generating redundant stores/reads for the simple caseswhere we will be able to match the keyword arguments directly against the keyword parameters. Not only are the stores/reads redundant, they also put extra pressure on the data flow computation, as we are generating many more cons-candidates.
In order to avoid this redundancy, we introduce a new type of content,
HashSplatContent
, which is used for the implicit argument stores (implicit-hash-arg
), but which are not allowed in reads out ofimplicit-hash-param
(KnownElementContent
reads should still be allowed in order to handle them1
example). This will effectively remove the redundancy in them4
example, but in order for reads such askwargs[:key1]
insidem2
to work, such reads should be able to match bothKnownElementContent
s andHashSplatContent
s, which we achieve usingContentSet::getAReadContent
.Splats
Unlike hash-splats, where we can simply merge all keywords arguments/parameters with all hash-splat arguments/parameters, flow through splats is limited to methods containing zero or more positional parameters followed by at most one splat parameter, and calls containing zero or more positional arguments followed by at most one splat argument (since we are not tracking the length of arrays):
As for hash-splats, the first case (
m1
) is handled by synthesizing an implicit splat parameterimplicit-splat-param
insidem1
, and then adding read steps out ofimplicit-splat-param
intop1
(resp.p2
), reading out aKnownElementContent
corresponding to the integer0
(resp.1
):The second case is handled dually: we synthesize an implicit splat argument
implicit-splat-arg
at the call site, and add store steps from the positional arguments intoimplicit-splat-arg
:The third case is handled using a combination, but care must be taken to shift indices both at the call site (where
a
must have index0
andb
must have index1
insideimplicit-splat-arg
), and similarly onlyb
must end up inrest
with re-shifted index0
.As for hash-splats, reusing
KnownElementContent
for the implicit stores/reads makes semantically sense, but introduces the same kind of redundancy and performance issues:The solution is similar: we introduce a new type of content,
SplatContent
, which is used for the implicit argument stores (implicit-splat-arg
), but we record not only the index, but also whether the index was shifted or not (in them3
example, onlyb
will have a shifted index). Then onlySplatContent
s with shifted indices (as well asKnownElementContent
s in them1
example) need to read out ofimplicit-splat-param
, as all other arguments will be able to match directly. Like forHashSplatContent
, we also need to ensure that reads such asrest[0]
insidem2
work, and we make use ofContentSet::getAReadContent
for this as well.