В ходе тестирования я заметил, что перебор связанных массивов в лучшем случае вдвое медленнее, чем при использовании внутренних методов доступа ( 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_ while
и method_ while2
, похоже, работают с связанным массивом в так же, как и цикл for
, но даже более медленный method_ while2
в два раза быстрее, чем связанный массив.
Даже добавление локализации $ _
и присвоения псевдонима method_ while2
в method_ while3
приводит к увеличению скорости выполнения на 66% по сравнению с связанным массивом.
Что еще в цикле for
происходит работа, чего нет в method_ while3
? Есть ли способ повысить скорость связанного массива?