像糊涂一样使用捕获

drc*_*law 8 capture perl6

我一直在阅读有关Captures的内容,这一段让我很感兴趣:

在签名内部,可以通过在无sigil参数的前面加上竖线|来创建捕获。这会将参数列表的其余部分打包到该参数中。

这听起来很像**@(不扁平化)粗鲁,所以编写了以下测试代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

0.82560328                                                                                                                                                                                                                                                                                                         
2.6650674 

Run Code Online (Sandbox Code Playgroud)

Capture显然具有性能优势。Capture以这种方式使用a 做为食肉有不利之处吗?我是否简化了比较?

Jon*_*ton 10

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;
}
Run Code Online (Sandbox Code Playgroud)

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.