代码之家  ›  专栏  ›  技术社区  ›  Ryan C. Thompson

在Perl中访问字符串中的单个字符时,substr还是拆分为数组更快?

  •  7
  • Ryan C. Thompson  · 技术社区  · 15 年前

    那么,使用起来更快吗 substr 一次提取一个字符,或者 split 将字符串放入数组中,然后在数组上迭代?

    当我在等待答案时,我想我将继续阅读如何在Perl中对事物进行基准测试。

    3 回复  |  直到 15 年前
        1
  •  9
  •   hobbs    15 年前

    这真的取决于你对你的数据做了什么——但是嘿,你的最后一个问题是正确的!别猜了,基准。

    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惊人的快。:)

        2
  •  4
  •   Sinan Ünür    15 年前

    下面是我要做的,而不是先在两者之间做出选择 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
  •  3
  •   brian d foy    15 年前

    我有个例子 掌握Perl 处理这个问题。您想创建一组单独的标量,每个标量都会带来Perl标量的内存开销,还是将所有内容存储在一个字符串中以减少内存,但可能要做更多的工作。你说你有很多这样的东西,所以如果你担心内存的话,把它们留作单个字符串可能会对你有更好的效果。

    如果您对基准测试和概要分析感兴趣的话,还将有几章介绍它们。

    以太说先让它工作,然后再担心其他的。其中一部分是将操作隐藏在面向任务的接口后面。一个好的面向对象模块可以为您做到这一点。如果你不喜欢这个方法,你可以改变它。但是,更高级别的程序不必更改,因为接口保持不变。