Использование замыканий для изменения классов внутри блоков Perl BEGIN - PullRequest
7 голосов
/ 07 марта 2012

ПРИМЕЧАНИЕ ВПЕРЕД: Пожалуйста, ради этого обсуждения давайте на мгновение проигнорируем тот факт, что тот же самый конец может быть достигнут при обращении к Class :: Accessor или даже просто с помощью Moose (вероятно, с лучшими результатами при учете читабельности и удобства сопровождения кода).

Что касается объектно-ориентированного Perl, в книге Programming Perl обсуждаетсяспособность генерировать методы доступа с замыканиями.Например, это допустимый фрагмент кода:

#!perl

use v5.12;
use warnings;

# at run-time
package Person1;

my @attributes = qw/name age address/;

for my $att ( @attributes )
{
  my $accessor = __PACKAGE__ . "::$att";

  no strict 'refs'; # allow symbolic refs to typeglob

  *$accessor = sub {
    my $self = shift;
    $self->{$att} = shift if @_;
    return $self->{$att};
  };
}

sub new { bless {}, shift }

package main;

use Data::Dumper;

my $dude = Person1->new;
$dude->name('Lebowski');
say Dumper($dude);

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

Теперь рассмотрим следующую альтернативу:

#!perl

use v5.12;
use warnings;

package Person2;

BEGIN
{
  for my $att (qw/name age address/)
  {
    my $accessor = __PACKAGE__ . "::$att";

    no strict 'refs'; # allow symbolic refs to typeglob

    *$accessor = sub {
      my $self = shift;
      $self->{$att} = shift if @_;
      return $self->{$att};
    };
  }
}

sub new { bless {}, shift }

package main;

use Data::Dumper;

my $dude = Person2->new;
$dude->name('Lebowski');
say Dumper($dude);

В этой версии композиция создается в блоке BEGIN (т.е., во время компиляции), и я считаю, что, решая эту задачу как можно скорее в жизненном цикле программы, я экономлю время при создании экземпляра объекта во время выполнения.

Простой Benchmark,

# benchmark it!
package main;

use Benchmark qw/cmpthese/;

cmpthese(-2, {
  accessors_new   => sub { Person1->new },
  accessors_begin => sub { Person2->new },
});

, похоже, подкрепляет мою теорию следующими результатами:

                    Rate accessors_begin   accessors_new
accessors_begin 853234/s              --             -9%
accessors_new   937924/s             10%              --

Предполагая, что мои рассуждения пока верны,

  • Что ещеСуществуют ли преимущества / недостатки при сравнении обеих этих стратегий?
  • Является ли хорошей идеей полагаться на блоки BEGIN в качестве эффективного способа выполнения такого рода манипуляций с классами?
  • Когда этоне рекомендуется?

Ответы [ 3 ]

10 голосов
/ 07 марта 2012

Когда я запускаю ваш тест, я испытываю некоторую дрожь, которая может объяснить ваши различия.Для разницы 10% или меньше, запустите ее несколько раз, чтобы быть уверенным.

                     Rate accessors_begin   accessors_new
accessors_begin 1865476/s              --             -4%
accessors_new   1943339/s              4%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1978799/s              --             -1%
accessors_new   2001062/s              1%              --

                     Rate   accessors_new accessors_begin
accessors_new   1943339/s              --             -2%
accessors_begin 1988089/s              2%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1796509/s              --             -8%
accessors_new   1949296/s              9%              --

                     Rate accessors_begin   accessors_new
accessors_begin 1916122/s              --             -3%
accessors_new   1969595/s              3%              --

Но на самом деле все, что вы тестируете, это sub new { bless {}, shift }.Сравнение того же самого с самим собой будет подчеркивать флаттер.Работа по созданию средств доступа уже была выполнена, когда код загружен и никогда не входит в него, начинается блок или нет.

Perl не имеет ни одного времени компиляции и времени выполнения.Скорее, каждая вещь use d, require d или eval ed проходит свои собственные шаги компиляции и выполнения.use Some::Class заставляет Some/Class.pm проходить как компиляцию, так и среду выполнения, выполняя BEGIN, компилируя подпрограммы и затем выполняя любой другой код.Независимо от того, находится ли код внутри или вне блока BEGIN в , модуль не имеет большого значения для кода вне этого модуля.

3 голосов
/ 07 марта 2012

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

Вы правильно говорите, что методы доступа создаются во время выполнения, но в коде, который вы показываете, они создаются один раз только в начале выполнения - конечно, не в момент создания экземпляра. Вы можете увидеть, что делает конструктор:

sub new { bless {}, shift }

, что довольно коротко и соответственно быстро. Применение блока BEGIN к циклу сборки средства доступа просто перемещает работу с начала времени выполнения до конца времени компиляции, и вы ничего не добились. Изменения, которые вы получаете в своих тестах, незначительны, и я полагаю, что в основном из-за шума.


Я воспроизвел ваши тесты на моей собственной системе, увеличив время выполнения до 10 секунд, и получил следующие результаты за четыре теста. Кажется, что добавление блока BEGIN немного повышает производительность, но это минимальное улучшение, и я не могу объяснить это немедленно.

                     Rate   accessors_new accessors_begin
accessors_new   1463771/s              --             -1%
accessors_begin 1476583/s              1%              --


                     Rate   accessors_new accessors_begin
accessors_new   1469833/s              --             -0%
accessors_begin 1472234/s              0%              --


                     Rate   accessors_new accessors_begin
accessors_new   1454942/s              --             -1%
accessors_begin 1469680/s              1%              --


                     Rate   accessors_new accessors_begin
accessors_new   1462613/s              --             -1%
accessors_begin 1473985/s              1%              --
1 голос
/ 07 марта 2012

Если вы разделяете пакеты на собственные файлы и используете use, разница отсутствует: код модуля запускается во время компиляции.

...