如何提高两个数据文件之间的效率(运行时间)

时间:2017-06-12 12:35:01

标签: perl

我有一个代码,用于比较两个文件之间某些特定术语的值。代码的主要耗时部分如下:

my @ENTIRE_FILE;
my %NETS;
my %COORDINATES;
my $INT=1;
my %IR_VALUES;
################################# READING
foreach my $IR_REPORT_FILE_1(@IR_REPORT_FILES){
   {
      open (FHIN, "<", $IR_REPORT_FILE_1) or die("Could not open $! for reading\n");
      # chomp(my @ENTIRE_FILE = <FHIN>);                            # READS THE ENTIRE FILE
      local undef $/;
      @ENTIRE_FILE = split(/\n(.*NET.*)/,<FHIN>);
      close (FHIN);
   }
   ############################### BUILDING HASH
   for my $i(1..$#ENTIRE_FILE/2){
     if($ENTIRE_FILE[$i*2-1]=~ /^----.*\s+"(\w+)"\s+/){
       my $net =$1;
       my @ir_values_of_net = split(/\n/,$ENTIRE_FILE[$i*2]);
       for my $val (@ir_values_of_net){
       push ( @{ $NETS{$INT}{$net} }, $val ) if ($val =~ /^r.*\s+m1|v1_viadg|v1_viabar|m2|ay_viabar|ay_viadg|c1\s+/); # NETS{1}{VDD}=array of values, NETS{1}{VSS}, NETS{1}{AVDD}
       }
     }
   }
   $INT++;                                          # For the next file:  NETS{2}{VDD}, NETS{2}{VSS}, NETS{2}{AVDD}
}
############################### COMPARISON
my $loop_count=0;
foreach my $net(keys %{ $NETS{1} }){
   print "net is $net\n";
   foreach my $file_1_net( @{ $NETS{1}{$net} }){
     my @sub_str_1 = split (' ', $file_1_net);
     foreach my $file_2_net ( @{ $NETS{2}{$net} } ){
       $loop_count++;
#        my @sub_str_1 = split (' ', $file_1_net);
       my @sub_str_2 = split (' ', $file_2_net);
       if(($sub_str_1[2] eq $sub_str_2[2])&&(($sub_str_1[3].$sub_str_1[4].$sub_str_1[5].$sub_str_1[6] eq $sub_str_2[3].$sub_str_2[4].$sub_str_2[5].$sub_str_2[6]) || ($sub_str_1[3].$sub_str_1[4].$sub_str_1[5].$sub_str_1[6] eq $sub_str_2[5].$sub_str_2[6].$sub_str_2[3].$sub_str_2[4]))){
         push (@{ $COORDINATES{$net}{X} },$sub_str_1[3],$sub_str_1[5]) if ($sub_str_1[3] && $sub_str_1[5]);
         push (@{ $COORDINATES{$net}{Y} },$sub_str_1[4],$sub_str_1[6]) if ($sub_str_1[4] && $sub_str_1[6]);
         my $difference=$sub_str_1[1]-$sub_str_2[1];
         if($sub_str_1[3]=~/^-/){
           push (@{ $MATCHED_RESISTORS{$net}{$sub_str_1[2].$sub_str_1[3].$sub_str_1[4].$sub_str_1[5].$sub_str_1[6]} }, $file_1_net,$file_2_net,$difference);
         }else{
           push (@{ $MATCHED_RESISTORS{$net}{$sub_str_1[2]."-".$sub_str_1[3].$sub_str_1[4].$sub_str_1[5].$sub_str_1[6]} }, $file_1_net,$file_2_net,$difference);
         }
         push (@{ $IR_VALUES{$net} }, $sub_str_2[1]);
         last;
       }
     }
   } 
   print max @{ $IR_VALUES{$net} };
   print "\nloop count is $loop_count\n";
   $loop_count = 0;
#    <>;  
}

我在代码上运行了一个分析器。以下是代码上面部分的输出:

snapshot from html report of the profiler

一些统计数据:

  1. 对于我的测试用例,最外层的foreach有3个元素。下面是 每次迭代的匹配元素数:
  2. element_1: 14
    element_1: 316
    element_1: 8
    
    1. 文件大小为8.3 MB和518.3KB。
    2. 整个代码的运行时间为:220s
    3. 我主要担心的是,当我的文件大小为每个8.3MB,并且两个文件之间的匹配数量更多时,运行时间很大,例如: 3小时。
    4. 我的问题很简单:如何让我的代码运行得更快?

      示例数据文件_1:

      r6_2389         1.29029e-05     ay_viabar       23.076   57.755   22.628   57.755   4.5      0        0        3.68449e-06      -5.99170336965613
      r6_2397         1.29029e-05     ay_viabar       22.948   57.755   22.628   57.755   4.5      0        0        3.68449e-06      -5.99170336965613
      r6_2400         1.29029e-05     ay_viabar       22.82    57.755   22.628   57.755   4.5      0        0        3.68449e-06      -5.99170336965613
      r6_2403         1.29029e-05     ay_viabar       22.692   57.755   22.628   57.755   4.5      0        0        3.68449e-06      -5.99170336965613
      r6_971          1.3279e-05      c1              9.492    45.742   -0.011   46.779   0.001    9.5589   10       0.0508653
      

      示例数据文件_2:

      r6_9261         0.00206167      ay_viabar       23.076   57.755   22.628   57.755   4.5      0        0        0.0207546    
      r6_9258         0.00206167      ay_viabar       22.948   57.755   22.628   57.755   4.5      0        0        0.0161057    
      r6_9399         0.00206167      ay_viabar       22.82    57.755   22.628   57.755   4.5      0        0        0.0127128    
      r6_9486         0.00206167      ay_viabar       22.692   57.755   22.628   57.755   4.5      0        0        0.0103186    
      r6_1061         1.3279e-05      cb_pc_viadg     -6.696   44.157   -0.159   44.847   0.001    0        0        0   
      

      示例输出:

         r6_9261         0.00206167      ay_viabar       23.076   57.755   22.628   57.755   4.5      0        0        0.0207546    
          r6_9258         0.00206167      ay_viabar       22.948   57.755   22.628   57.755   4.5      0        0        0.0161057    
          r6_9399         0.00206167      ay_viabar       22.82    57.755   22.628   57.755   4.5      0        0        0.0127128    
          r6_9486         0.00206167      ay_viabar       22.692   57.755   22.628   57.755   4.5      0        0        0.0103186 
      

      示例输出基本上被推入另一个进一步处理的哈希。但是根据分析器,构建此哈希消耗的总运行时间约占总运行时间的90%。

1 个答案:

答案 0 :(得分:2)

好的,所以我的第一个想法是 - 你有一个3深的循环,这将永远是低效的。我们可能会以很快的速度交换内存。

假设“更大”&#39;文件是&#39; sample_1&#39;,否则交换它们。

在此示例中 - sample_2将消耗与行数成比例的内存 - 因此我们理想地希望它是较小的文件。您可能需要交换匹配/测试,具体取决于file1 cols 5,6,3,4是否与file2匹配,反之亦然。

但希望这说明一个有用的概念来解决你的问题,如果不是完全解决它?

这样的事情可以解决问题:

#!/usr/bin/env perl

use strict;
use warnings;

my %is_match; 

open ( my $sample_1, '<', 'sample1.txt' ) or die $!;
open ( my $sample_2, '<', 'sample2.txt' ) or die $!;

#    first of all, column 2 , 3,4,5,6 should match between 2 files. 
# and then print out both matching lines from two files.     
#    column 3,4,5,6 from one file can match with column 5,6,3,4. 

while ( <$sample_2> ) { 
   my @row = split; 
   #insert into hash
   #this would be much clearer if the fields were named rather than numbered
   #I think. 
   $is_match{$row[3]}{$row[4]}{$row[5]}{$row[6]}++; 
   $is_match{$row[5]}{$row[6]}{$row[3]}{$row[4]}++; 
}

while ( <$sample_1> ) {
   my @row = split;
   #print the current line if it matches from the hash above.
   print if $is_match{$row[3]}{$row[4]}{$row[5]}{$row[6]};
}

因为这会迭代每个文件一次,所以它应该快得多。并且因为你的一个文件很小,那么你应该先读入一个文件到内存中。

使用提供的样本数据,可以获得所需的输出。

第一个循环读取文件,选择您感兴趣的字段并根据您的4个键将它们插入到散列中。

然后它再次为另一组有效匹配键执行此操作。

第二个循环读取另一个文件,选择密钥并检查散列中是否存在任何组合。并打印出当前行。