在perl脚本中无法在更短的时间内打印多个记录

时间:2013-01-18 14:47:31

标签: perl

我有perl脚本,用于日期显示信息。 我根据日期获取数据并将其保存为具有字段分隔符“AaBbCc”的平面文件。

文件名将是

18-01-13_REPORT
17-01-13_REPORT

记录将是

111 AaBbCc 2222 AaBbCc 3333 AaBbCc etc(each two lines forms a single record)

每个文件都有超过5000条记录。使用我的代码我能够成功打印记录。 但是打印所有记录需要15分钟。 现在我想优化代码,以便在更短的时间内打印这些记录。

请找到我的代码

open (FILE,"$filename/$val_REPORT_DATE") 
    or die "Could not read from $filename, program halting.";
local $/ = undef; #get the whole file in to picture
while(<FILE>) {
    chomp $_;
    @fields = split('AaBbCc', $_);
    for ( $i=0 ; $i<$count ; ) {
        print "<tr><td>" . $fields[$i+0] .
              "</td><td>". $fields[$i+1] .
              "</td><td>". $fields[$i+2] .
              "</td><td>". $fields[$i+3] .
              "</td><td>". $fields[$i+4] .
              "</td><td>". $fields[$i+5] .
              "</td><td>". $fields[$i+6] .
                           $fields[$i+7] ."</td></tr>";
        $i = $i + 8;
    }
}

请帮我在短时间内打印所有记录以提高性能

提前致谢!!! 维杰

1 个答案:

答案 0 :(得分:2)

您的代码速度缓慢有几个原因:

  1. 使用的文件格式是braindead。
  2. 对于您在@fields中查找的每个元素,都会执行添加。虽然在低级语言中这不会过于昂贵,但Perls Scalars非常昂贵。

    以下是您的操作码树中执行数组元素查找的许多部分之一

    35               <2> aelem sK/2 ->36
    31                  <1> rv2av sKR/1 ->32        # get actual array
    30                      <#> gv[*fields] s ->31  # get global typeglob
    34                  <2> add[t63] sK/2 ->35      # create new scalar here
    -                       <1> ex-rv2sv sK/1 ->33  # get actual scalar
    32                         <#> gvsv[*i] s ->33  # get global typeglob
    33                      <$> const[IV 7] s ->34
    

    将此与具有词汇变量的元素查找进行比较,并且不添加:

    c        <2> aelem sK/2 ->d
    a           <0> padav[@fields:47,49] sR ->b     # beautiful
    b           <0> padsv[$i:48,49] s ->c           # beautiful
    
  3. 有一些最佳做法可能会产生更高效的代码,例如:使用my的词法变量。全局变量的查找方式不同,而且速度要慢得多(见上文)。此外,不需要立即在整个文件中啜饮。如果可以在恒定的空间内完成,为什么要使用那么多内存呢?

  4. #!/usr/bin/perl
    use strict; use warnings; # every good script starts with these
    use 5.010;
    
    open my $file, "<", $filename or die qq(Couldn't open "$filename": $!):
    until (eof $file) {
      # read two lines at a time
      my $line1 = <$file>;
      my $line2 = <$file> // die qq(uneven number of lines in "$filename");
      ...
    }
    

    现在我们可以填写两种可能的解决方案。这是一个强调数据流编程的方法(从下往上阅读):

    print
        "<tr>" . (
           join "" =>
           map  "<td>$_</td>",
           map  {chomp; split /AaBbCc/}
                ($line1, $line2)
        ) . "</tr>\n"
        ;
    

    相同的算法可以编码为

    chomp($line1, $line2);
    my $string = "";
    $string .= "<td>$_</td>" for split(/AaBbCc/, $line1), split(/AaBbCc/, $line2);
    print "<tr>$string</tr>\n";
    

    我们也可以滥用特殊变量:

    chomp($line1, $line2);
    my @fields = split(/AaBbCc/, $line1), split(/AaBbCc/, $line2);
    local $" = "</td><td>"; # list seperator
    print "<tr><td>@fields</td></tr>\n";
    

    或者,没有命名数组:

    chomp($line1, $line2);
    local $" = "</td><td>";
    print "<tr><td>@{[split(/AaBbCc/, $line1), split(/AaBbCc/, $line2)]}</td></tr>\n";
    

    我不做的是手动计算索引或展开循环。

    现在虽然无法保证这些变体运行得更快,但您可以尝试一些材料。要真正优化您的代码,您应该转向Devel::NYTProf分析器。它生成非常详细的逐行报告,显示每个语句执行的次数以及平均花费的时间。


    假设您的所有字段都没有包含标签,这是一个将数据转换为合理的标签分隔输出的脚本:

    #!/usr/bin/perl
    use strict; use warnings; use feature 'say';
    
    # usage perl convert.pl filenames...
    
    for my $filename (@ARGV) {
      open my $oldfile, "<", $filename       or die qq(Can't open "$filename": $!);
      open my $newfile, ">", "$filename.new" or die qq(Can't open "$filename.new": $!);
      until (eof $oldfile) {
         my $line1 = <$oldfile> // die qq(unexpected eof in "$filename");
         my $line2 = <$oldfile> // die qq(unexpected eof in "$filename": uneven number of lines);
         chomp( $line1, $line2 );
         my @fields = map {split /AaBbCc/, $_, 4} $line1, $line2;
         say $newfile join "\t" => @fields;
      }
      rename $filename       => "$filename.bak" or die qq(Can't back up "$filename");
      rename "$filename.new" => $filename       or die qq(Can't replace "$filename" with transformed data);
    }