像 Slurpy 一样使用 Capture

Using a Capture like a Slurpy

我一直在阅读有关 Captures 的文章,这一段引起了我的兴趣:

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.

这听起来很像 **@(非扁平化)泥浆,所以编造了这个测试代码:

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;
}

一个样本运行给出了每个测试块的持续时间:

0.82560328                                                                                                                                                                                                                                                                                                         
2.6650674 

Capture 似乎确实具有性能优势。以这种方式使用 Capture 作为 slurpy 有不利的一面吗?我是否过度简化了比较?

A Capture 有两个槽,分别是 VM 级数组(位置参数)和散列(命名参数)。它的构造非常便宜,并且 - 由于 |c 样式参数在内部的各个位中很常见 - 已经得到很好的优化。由于捕获参数吞噬了位置参数和命名参数,因此任何命名参数都将被静默忽略。对于方法来说,这可能不是什么大问题,无论如何,不​​需要的命名参数将被静默地放入 %_,但如果在 sub 上使用此构造可能是一个考虑因素,因为它不是纯粹的优化:它改变了行为。

**@c 案例分配一个 Array,然后为每个传递的值分配一个 Scalar 容器,将它们放入 Scalar 容器和那些 Scalar 容器放入 Array。这是合理的额外工作量。

这里没有考虑另一种情况,就是这个:

sub test3(**@c is raw){
    1;
}

@c中放置一个List,并设置它的元素直接引用传递的东西。这比没有is raw的情况便宜了一点。从理论上讲,它的性能可能与 |c 这样的捕获参数一样好(如果不是更好的话);它可能只需要有人在编译器上工作来深入研究为什么它还没有。

总而言之,如果不关心具有可变 Array 传入参数的强制分项 and/or,那么添加 is raw 可能是比选择捕获参数更好的优化选择:参数处理语义更接近,它已经有点快了,将允许更自然的代码,并且未来有可能与 |c.

一样快,如果不是更快的话。