В моем тестировании я заметил, что перебор связанных массивов в лучшем случае вдвое быстрее, чем при использовании внутренних методов доступа (FETCH
и FETCHSIZE
). Следующий тест показывает проблему:
{package Array;
sub new {
my $class = shift;
tie my @array, $class, [@_];
\@array
}
sub TIEARRAY {
my ($class, $self) = @_;
bless $self => $class;
}
sub FETCH {$_[0][$_[1]]}
sub FETCHSIZE {scalar @{$_[0]}}
}
use List::Util 'sum';
use Benchmark 'cmpthese';
for my $mag (map 10**$_ => 1 .. 5) {
my $array = Array->new(1 .. $mag);
my $tied = tied(@$array);
my $sum = sum @$array;
print "$mag: \n";
cmpthese -2 => {
tied => sub {
my $x = 0;
$x += $_ for @$array;
$x == $sum or die $x
},
method => sub {
my $x = 0;
$x += $tied->FETCH($_) for 0 .. $tied->FETCHSIZE - 1;
$x == $sum or die $x
},
method_while => sub {
my $x = 0;
my $i = 0; $x += $tied->FETCH($i++) while $i < $tied->FETCHSIZE;
$x == $sum or die $x
},
method_while2 => sub {
my $x = 0;
my $i = 0;
$x += tied(@$array)->FETCH($i++)
while $i < tied(@$array)->FETCHSIZE;
$x == $sum or die $x
},
method_while3 => sub {
my $x = 0;
my $i = 0;
while ($i < tied(@$array)->FETCHSIZE) {
local *_ = \(tied(@$array)->FETCH($i++));
$x += $_
}
$x == $sum or die $x
},
};
print "\n";
}
При размере массива 1000 эталонный тест возвращает:
1000:
Rate tied method_while3 method_while2 method_while method
tied 439/s -- -40% -51% -61% -79%
method_while3 728/s 66% -- -19% -35% -65%
method_while2 900/s 105% 24% -- -19% -57%
method_while 1114/s 154% 53% 24% -- -47%
method 2088/s 375% 187% 132% 87% --
Я пропустил другие прогоны, потому что размер массива не приводит к значительному изменению относительных скоростей.
method
, конечно, самый быстрый, потому что он не проверяет размер массива на каждой итерации, однако method_while
и method_while2
, похоже, работают с связанным массивом так же, как for
loop, но даже медленнее method_while2
в два раза быстрее связанного массива.
Даже добавление локализации $_
и присвоения с псевдонимом к method_while2
в method_while3
приводит к быстрому выполнению на 66% по сравнению с связанным массивом.
Какая дополнительная работа происходит в цикле for
, чего не происходит в method_while3
? Есть ли способ улучшить скорость связанного массива?