При доступе к отдельным символам в строке в Perl, является ли substr или разбиение на массив быстрее? - PullRequest
7 голосов
/ 21 октября 2010

Я пишу Perl-скрипт, в котором мне нужно циклически проходить каждый символ строки.Строк много, и каждая имеет длину 100 символов (это короткие последовательности ДНК, если вам интересно).

Итак, быстрее ли использовать substr для извлечения каждого символа по одномувремя, или это быстрее split строка в массив и затем итерации по массиву?

Пока я жду ответа, я думаю, что я прочитал о том, как тестироватьвещи в Perl.

Ответы [ 3 ]

9 голосов
/ 21 октября 2010

Это действительно зависит от того, что именно вы делаете со своими данными - но, эй, вы правильно направили свой последний вопрос!Не угадайте, бенчмарк.

Perl предоставляет модуль Benchmark именно для такого рода вещей, и использовать его действительно довольно просто.Вот небольшой пример кода, с которого нужно начать:

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw(cmpthese);

my $dna;
$dna .= [qw(G A T C)]->[rand 4] for 1 .. 100;

sub frequency_substr {
  my $length = length $dna;
  my %hist;

  for my $pos (0 .. $length) {
    $hist{$pos}{substr $dna, $pos, 1} ++;
  }

  \%hist;
}

sub frequency_split {
  my %hist;
  my $pos = 0;
  for my $char (split //, $dna) {
    $hist{$pos ++}{$char} ++;
  }

  \%hist;
}

sub frequency_regmatch {
  my %hist;

  while ($dna =~ /(.)/g) {
    $hist{pos($dna)}{$1} ++;
  }

  \%hist;
}


cmpthese(-5, # Run each for at least 5 seconds
  { 
    substr => \&frequency_substr,
    split => \&frequency_split,
    regex => \&frequency_regmatch
  }
);

И пример результата:

         Rate  regex  split substr
regex  6254/s     --   -26%   -32%
split  8421/s    35%     --    -9%
substr 9240/s    48%    10%     --

Оказывается, substr работает на удивление быстро.:)

4 голосов
/ 21 октября 2010

Вот что я хотел бы сделать вместо первой попытки выбора между substr и split:

#!/usr/bin/perl

use strict; use warnings;

my %dist;
while ( my $s = <> ) {
    while ( $s =~ /(.)/g ) {
        ++ $dist{ pos($s) }{ $1 };
    }
}

Обновление:

Мое любопытство одержало верх. Вот эталон:

#!/usr/bin/perl

use strict; use warnings;
use Benchmark qw( cmpthese );

my @chars = qw(A C G T);
my @to_split = my @to_substr = my @to_match = map {
    join '', map $chars[rand @chars], 1 .. 100
} 1 .. 1_000;

cmpthese -1, {
    'split'  => \&bench_split,
    'substr' => \&bench_substr,
    'match'  => \&bench_match,
};

sub bench_split {
    my %dist;
    for my $s ( @to_split ) {
        my @s = split //, $s;
        for my $i ( 0 .. $#s ) {
            ++ $dist{ $i }{ $s[$i] };
        }
    }
}

sub bench_substr {
    my %dist;
    for my $s ( @to_substr ) {
        my $u = length($s) - 1;
        for my $i (0 .. $u) {
            ++ $dist{ $i }{ substr($s, $i, 1) };
        }
    }
}

sub bench_match {
    my %dist;
    for my $s ( @to_match ) {
        while ( $s =~ /(.)/g ) {
            ++ $dist{ pos($s) }{ $1 };
        }
    }
}

Выход:

         Rate  split  match substr
split  4.93/s     --   -31%   -65%
match  7.11/s    44%     --   -49%
substr 14.0/s   184%    97%     --
3 голосов
/ 21 октября 2010

У меня есть пример в Мастеринг Perl , касающийся этой проблемы.Хотите ли вы создать группу отдельных скаляров, каждый из которых несет в себе накладные расходы памяти скаляра Perl, или хранить все в одной строке, чтобы уменьшить объем памяти, но, возможно, выполнять больше работы.Вы говорите, что у вас их много, поэтому если вы беспокоитесь о памяти, то лучше оставить их в виде одиночных строк.

Освоение Perl также содержит несколько глав, посвященныхс бенчмаркингом и профилированием, если вам это интересно.

Эфир говорит, чтобы сначала он заработал, а потом побеспокоился обо всем остальном.Частично это скрывает операции за ориентированным на задачи интерфейсом.Хороший объектно-ориентированный модуль может сделать это за вас.Если вам не нравится имплментация, вы меняете ее.Тем не менее, программы на более высоком уровне не должны меняться, потому что интерфейс остается прежним.

...