Ruby makes it easy to delegate all arguments from one method to another:
deff(*args,**kw)g(*args,**kw)end
Unfortunately, this indirection decreases performance. One reason it
decreases performance is that this allocates an array and a hash per
call to f, even if args and kw are not modified.
Due to Ruby's ability to modify almost anything at runtime, it's
difficult to avoid the array allocation in the general case. For
example, it's not safe to avoid the allocation in a case like this:
deff(*args,**kw)foo(bar)g(*args,**kw)end
Because foo may be eval and bar may be a string referencing args
or kw.
To fix this correctly, you need to perform something similar to escape
analysis on the variables. However, there is a case where you can
avoid the allocation without doing escape analysis, and that is when
the splat variables are anonymous:
deff(*,**)g(*,**)end
When splat variables are anonymous, it is not possible to reference
them directly, it is only possible to use them as splats to other
methods. Since that is the case, if f is called with a regular
splat and a keyword splat, it can pass the arguments directly to g without copying them, avoiding allocation. For example:
I call this technique: Allocationless Anonymous Splat Forwarding.
This is implemented using a couple additional iseq param flags,
anon_rest and anon_kwrest. If anon_rest is set, and an array splat
is passed when calling the method when the array splat can be used
without modification, setup_parameters_complex does not duplicate
it. Similarly, if anon_kwest is set, and a keyword splat is passed
when calling the method, setup_parameters_complex does not
duplicate it.
Introduce Allocationless Anonymous Splat Forwarding
Ruby makes it easy to delegate all arguments from one method to another:
Unfortunately, this indirection decreases performance. One reason it
decreases performance is that this allocates an array and a hash per
call to
f
, even ifargs
andkw
are not modified.Due to Ruby's ability to modify almost anything at runtime, it's
difficult to avoid the array allocation in the general case. For
example, it's not safe to avoid the allocation in a case like this:
Because
foo
may beeval
andbar
may be a string referencingargs
or
kw
.To fix this correctly, you need to perform something similar to escape
analysis on the variables. However, there is a case where you can
avoid the allocation without doing escape analysis, and that is when
the splat variables are anonymous:
When splat variables are anonymous, it is not possible to reference
them directly, it is only possible to use them as splats to other
methods. Since that is the case, if
f
is called with a regularsplat and a keyword splat, it can pass the arguments directly to
g
without copying them, avoiding allocation. For example:I call this technique: Allocationless Anonymous Splat Forwarding.
This is implemented using a couple additional iseq param flags,
anon_rest and anon_kwrest. If anon_rest is set, and an array splat
is passed when calling the method when the array splat can be used
without modification,
setup_parameters_complex
does not duplicateit. Similarly, if anon_kwest is set, and a keyword splat is passed
when calling the method,
setup_parameters_complex
does notduplicate it.