I've been reading about Captures and this paragraph intrigued me:
Inside a Signature, a Capture may be created by prefixing a sigilless parameter with a vertical bar |. This packs the remainder of the argument list into that parameter.
This sounds a lot like a **@
(non flattening) slurpy, so this concocted this test code:
my $limit=1_000_000;
my @a=1 xx 10000;
my @b=-1 xx 10000;
sub test1(|c){
1;
};
sub test2(**@c){
1;
};
{
for ^$limit {
test1(@b,@a);
}
say now - ENTER now;
}
{
for ^$limit {
test2(@b,@a);
}
say now - ENTER now;
}
A sample run gives durations of each test block:
0.82560328
2.6650674
The Capture certainly seems to have the performance advantage. Is there a down side to using a Capture
as a slurpy in this fashion? Have I over simplified the comparison?
A Capture
has two slots, holding a VM-level array (positional arguments) and hash (named arguments). It is quite cheaply constructed, and - since |c
style arguments are quite common in various bits of the internals - has been well optimized. Since a capture parameter slurps up both positional and named arguments, any named arguments will be silently ignored. That's probably not much of an issue for methods, where unrequired named arguments will be silently placed into %_
anyway, but might be a consideration if using this construct on a sub
, since it's not a pure optimization: it changes behavior.
The **@c
case allocates an Array
, and then allocates a Scalar
container for each of the passed values, placing them into the Scalar
containers and those Scalar
containers into the Array
. That's a reasonable amount of extra work.
There's another case not considered here, which is this one:
sub test3(**@c is raw){
1;
}
That places a List
in @c
, and sets its elements to refer directly to the things that were passed. This is a bit cheaper than the case without is raw
. In theory, it could probably perform as well as - if not better than - a capture parameter like |c
; it probably just needs somebody working on the compiler to have a dig into why it doesn't yet.
In summary, if not caring about enforcing itemization and/or having a mutable Array
of incoming arguments, then adding is raw
is probably a better optimization bet than picking a capture parameter: the argument processing semantics are closer, it's already a bit faster, will allow for more natural code, and has future potential to be every bit as fast as, if not faster, than |c
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With