Используя Захват как Slurpy - PullRequest
8 голосов
/ 16 апреля 2019

Я читал о Captures , и этот параграф заинтриговал меня:

Внутри подписи, Capture может быть создан путем добавления префикса параметра без знака к вертикальной черте |,Это упаковывает оставшуюся часть списка аргументов в этот параметр.

Это звучит очень похоже на **@ (не сплющивающее) невнятное значение, поэтому это состряпало этот тестовый код:

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 в качестве слизи таким способом?Я упростил сравнение?

1 Ответ

10 голосов
/ 17 апреля 2019

A Capture имеет два слота, содержащих массив уровня VM (позиционные аргументы) и хэш (именованные аргументы). Он довольно дешево сконструирован и - поскольку аргументы стиля |c довольно часто встречаются в различных элементах внутреннего интерфейса - был хорошо оптимизирован. Поскольку параметр захвата нарушает как позиционные, так и именованные аргументы, любые именованные аргументы будут игнорироваться. Это, вероятно, не является большой проблемой для методов, где необязательные именованные аргументы будут все равно тихо помещены в %_, но это может быть соображением, если использовать эту конструкцию для sub, поскольку это не чистая оптимизация: она меняет поведение.

Случай **@c выделяет Array, а затем выделяет контейнер Scalar для каждого из переданных значений, помещая их в контейнеры Scalar и эти контейнеры Scalar в Array. Это разумное количество дополнительной работы.

Есть еще один случай, который здесь не рассматривается, а именно:

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

Это помещает List в @c и устанавливает его элементы для прямой ссылки на то, что было передано. Это немного дешевле, чем в случае без is raw. Теоретически, он мог бы работать так же хорошо, если не лучше, как параметр захвата, такой как |c; возможно, просто нужен кто-то, работающий над компилятором, чтобы понять, почему он этого еще не делает.

Таким образом, если не заботиться о принудительной детализации и / или иметь изменяемые Array входящих аргументов, то добавление is raw, вероятно, является лучшим выбором для оптимизации, чем выбор параметра захвата: семантика обработки аргументов ближе, это уже немного быстрее, позволит создавать более естественный код и в будущем может стать таким же быстрым, как если бы не быстрее, чем |c.

...