Снижение производительности при использовании отражения $ foo -> $ bar () - PullRequest
5 голосов
/ 02 декабря 2011

Я хотел бы знать, что именно происходит, когда я использую отражение, чтобы вызвать метод, имя которого у меня есть в виде строки:

my $foo = Foo->new();
my $method = 'myMethod';
$foo->$method();

на ~ 20% медленнее, чем собственный вызов:

$foo->myMethod();

Любые указатели на документацию о том, как реализовано отражение в Perl, были бы полезны.

Спасибо.

Ответы [ 3 ]

10 голосов
/ 02 декабря 2011
> perl -MO=Concise -e '$foo->$bar'
8  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
7     <1> entersub[t3] vKS/TARG ->8
3        <0> pushmark s ->4
-        <1> ex-rv2sv sKM/1 ->5
4           <#> gvsv[*foo] s ->5
6        <1> method K/1 ->7             # ops to read $bar and then call method
-           <1> ex-rv2sv sK/1 ->6       # 
5              <#> gvsv[*bar] s ->6     # 
-e syntax OK

> perl -MO=Concise -e '$foo->bar'
7  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
6     <1> entersub[t2] vKS/TARG ->7
3        <0> pushmark s ->4
-        <1> ex-rv2sv sKM/1 ->5
4           <#> gvsv[*foo] s ->5
5        <$> method_named[PV "bar"] ->6   # op to call the 'bar' method
-e syntax OK

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

Во втором примере perl уже знает, что в качестве имени метода должна использоваться строка "bar", поэтомуизбегает загрузки переменной и проверки ее содержимого при каждом выполнении.

Но вы не должны слишком беспокоиться о разнице в скорости между двумя нативными операциями на 20%.Главным образом потому, что нативные операции очень быстрые, и любая скорость, которая им действительно требуется, будет быстро уменьшена фактическим алгоритмом, который должен выполнять ваш код.Другими словами, если вы не выделили эту проблему как горячую точку с помощью профилировщика кода, разница в скорости имеет скорее педагогическое, чем практическое значение.

4 голосов
/ 03 декабря 2011

Во-первых, я не доверяю тестам, которые не вижу. Слишком легко понять их неправильно. Я тестировал их сам.

use strict;
use warnings;

use Benchmark qw( cmpthese );

sub new { return bless({}, $_[0]); }
sub myMethod { }

my %tests = (
   rt => '$foo->$method()  for 1..1000;',
   ct => '$foo->myMethod() for 1..1000;',
);

$_ = 'use strict; use warnings; our $foo; our $method; ' . $_
   for values(%tests);

our $foo = __PACKAGE__->new();
our $method = 'myMethod';

cmpthese(-3, \%tests);

Я могу повторить ваши результаты.

     Rate   rt   ct
rt 1879/s   -- -19%
ct 2333/s  24%   --

(Rate is 1/1000th of actual rate.)

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

Compile-time: 2333000 calls per second = 429 nanoseconds per call
Run-time:     1879000 calls per second = 532 nanoseconds per call
Difference:   103 nanoseconds per call.

Не так много. Так где же это время проведено?

$ perl -MO=Concise,-exec -e'$foo->myMethod()'     $ perl -MO=Concise,-exec -e'$foo->$method()'
1  <0> enter                                   =  1  <0> enter 
2  <;> nextstate(main 1 -e:1) v:{              =  2  <;> nextstate(main 1 -e:1) v:{
3  <0> pushmark s                              =  3  <0> pushmark s
4  <#> gvsv[*foo] s                            =  4  <#> gvsv[*foo] s
                                               +  5  <#> gvsv[*method] s
5  <$> method_named[PV "myMethod"]             !  6  <1> method K/1
6  <1> entersub[t2] vKS/TARG                   =  7  <1> entersub[t3] vKS/TARG
7  <@> leave[1 ref] vKP/REFC                   =  8  <@> leave[1 ref] vKP/REFC
-e syntax OK                                   =  -e syntax OK

Кажется, единственное отличие - это поиск в таблице дополнительных символов. 100 нс кажется чрезмерным для этого. Но чтобы быть уверенным, сравните с чем-то крошечным, скажем, как добавление одного.

$ perl -MO=Concise,-exec -e'my $y = $x;'     $ perl -MO=Concise,-exec -e'my $y = $x + 1;'
1  <0> enter                              =  1  <0> enter 
2  <;> nextstate(main 1 -e:1) v:{         =  2  <;> nextstate(main 1 -e:1) v:{
3  <#> gvsv[*x] s                         =  3  <#> gvsv[*x] s
                                          +  4  <$> const[IV 1] s
                                          +  5  <2> add[t3] sK/2
4  <0> padsv[$y:1,2] sRM*/LVINTRO         =  6  <0> padsv[$y:1,2] sRM*/LVINTRO
5  <2> sassign vKS/2                      =  7  <2> sassign vKS/2
6  <@> leave[1 ref] vKP/REFC              =  8  <@> leave[1 ref] vKP/REFC
-e syntax OK                              =  -e syntax OK

Подставляя этот код и our $x = 100; в код теста выше, мы получаем

            Rate addition  baseline
addition  4839/s       --      -26%
baseline  6532/s      35%        --

(Rate is 1/1000th of actual rate.)

Итак,

Basline:    6553000/s = 153 nanoseconds per assignment
Addition:   4839000/s = 207 nanoseconds per assignment+addition
Difference:              54 nanoseconds per addition

Так разумно ли, чтобы простой просмотр таблицы символов занимал вдвое больше времени, чем добавление одного? Возможно, так как это включает хеширование строки и поиск строки в коротком связанном списке.

Вы действительно хотите потратить лишние 100 нс здесь и там? Нет, наверное.

1 голос
/ 02 декабря 2011

Вы могли бы ускорить это, используя ссылку на метод, ala:

$metref = \&{"Class::$method"};
$instance = new Class;
$instance->$metref(@args);

Вы, очевидно, могли бы использовать $metref = \&Class::myMethod вместо этого, если вы знали имя метода во время компиляции.Есть также замыкания, использующие sub { ... }, которые perl может обрабатывать более эффективно, чем ваша символическая разыменование.См. это обсуждение perlmonks и perlref: Создание ссылок .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...