Почему связанные массивы в Perl такие медленные ?

В ходе тестирования я заметил, что перебор связанных массивов в лучшем случае вдвое медленнее, чем при использовании внутренних методов доступа ( 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 ? Есть ли способ повысить скорость связанного массива?

8
задан Gabe 2 April 2011 в 08:05
поделиться