在Perl中处理15亿行的文件

如何解决在Perl中处理15亿行的文件

我需要处理一个包含15亿个条目的文件,其中包含11列,大小为300GB。我需要从每行中提取一些信息。 我已将问题分为两部分。首先,我阅读了文件,并尝试将文件缩小为我需要的列和一些过滤条件。我写出了这个所谓的精简文件〜700GB约100GB。项。

然后,我阅读精简文件并处理每个条目。这是我的代码段:

while (my $line = <$fh_file_vss_rlrp_inst>) {
  chomp $line;
  my @columns_line = split('\s+',$line);
  if( scalar @columns_line == 10 && $columns_line[-1] !~ /X|Y|Z|K/ ) {
    print $fh_reduced "$columns_line[1] $columns_line[7] $columns_line[-1]\n";
  } 
  $inst_count++;
   if($inst_count % 10000000 == 0) { 
     $date = `date`;
     print "Processed $inst_count ... time: $date";
   }
}

现在正在读取缩小的文件以进行进一步处理,我的%h_kI_vB内存中有100,000个条目:

while (my $line = <$fh_file_vss_rlrp_inst>) {
chomp $line;
my @columns_line = split('\s+',$line);
  my $wip = $columns_line[0];
  my $this_inst = $columns_line[0];
  my @loc_xyz;
  my @loc_ijk;
  while (grep(/\//,$wip)){
      if (exists($h_kI_vB{$wip})){ 
          push(@loc_xyz,$h_kI_vB{$wip});                         
      }
      $wip =~ s/\/[^\/]+$//;
  }
  if (exists($h_kI_vB{$wip})){
      push(@loc_xyz,$h_kI_vB{$wip});
  }
  
  if (@loc_xyz){
      my $loc_block_string;
      foreach my $loc ( @loc_xyz){
          if (not defined $loc_block_string){
              $loc_block_string = "$loc";
          } else {
              $loc_block_string = $loc_block_string.":$loc";
          }
      }
      $hierarchical_blocks{$this_inst}=$loc_block_string;
      
  }
  if(exists $hierarchical_blocks{$this_inst}) {
    my @x_y_layer = split(',',$columns_line[1]);
    $x_y_layer[0] =~ s/\(//;
    if(! defined $H_instBlock_coordinates{ $hierarchical_blocks{$this_inst} } ) { 
      my @initial_coordinate = ($x_y_layer[0],$x_y_layer[1],$x_y_layer[0],$x_y_layer[1]);
      print $fh_log "For inst:$this_inst $hierarchical_blocks{$this_inst}: @initial_coordinate\n" ;
      @{ $H_instBlock_coordinates{$hierarchical_blocks{$this_inst}} } = @initial_coordinate; 
    } else {
      my @old_x1_y1_x2_y2 =  @{ $H_instBlock_coordinates{$hierarchical_blocks{$this_inst}} };
      my $x1 = $old_x1_y1_x2_y2[0];
      my $y1 = $old_x1_y1_x2_y2[1];
      my $x2 = $old_x1_y1_x2_y2[2];
      my $y2 = $old_x1_y1_x2_y2[3];
      if($x_y_layer[0] < $x1) {
        $x1 = $x_y_layer[0];
      } elsif ($x_y_layer[0] > $x2) {
        $x2 = $x_y_layer[0];
      }
      
      if($x_y_layer[1] < $y1) {
        $y1 = $x_y_layer[1];
      } elsif ($x_y_layer[1] > $y2) {
        $y2 = $x_y_layer[1];
      }
      
      my @new_x1_y1_x2_y2 = ($x1,$y1,$x2,$y2);
      
      print $fh_log "For inst:$this_inst Changing coordinate to block $hierarchical_blocks{$this_inst}: @new_x1_y1_x2_y2\n" ;
      @{ $H_instBlock_coordinates{$hierarchical_blocks{$this_inst}} } = @new_x1_y1_x2_y2; 
      
    }
 }
  
 $inst_count++;
 if($inst_count % 1000000 == 0) {
   $date = `date`;
   print "Processed $inst_count ...time: $date\n";
 }  
 }

这太慢了。我将作业分发到具有450GB内存的远程服务器,该服务器运行24小时。 我需要优化代码,以使其在1小时内完成(最坏的情况)。

谢谢。

解决方法

通过从根本上改变自己的方法,可以获得真正的性能提升。由于您拥有简单的O(N)算法,因此该部门中没有什么特别突出的。

下面,我清理了您的代码并提供了一些微优化。但是我怀疑他们会造成很大的损失。原因是您的代码已经非常快了。您说处理700,000,000条线需要24小时,这意味着每条线仅需要12μs的时间即可处理。这是合理的。

  24 / 700,000 hour/line
* 60 minute/hour
* 60 s/minute
= 1.2 * 10^(-4) s/line
= 12 μs/line

您可能会从并行化中获益。例如,没有什么可以阻止第二个程序与第一个程序同时运行,而是使用两个内核而不是1个内核。它看起来像这样:

./prog_a | ./prog_b

进一步并行化此过程的复杂性在于,处理一行依赖于早期行的处理输出。

尽管如此,将$block的处理从prog_a移到prog_b可能会更有利,甚至可能在管道中创建一个中间阶段。

./prog_a | ./prog_i | ./prog_b

由您自己决定要在阶段数和每个阶段所执行的操作之间取得平衡,以达到最佳效果。例如,我猜想将原始文件的第10个字段的分析移到prog_a会更有利,因此在我发布的版本中将其从prog_b移到prog_a在下面。

但是工作仍然大部分是连续的。下一步是将使用$block的工作分配到多个内核中。只要具有相同值$block的行最终被同一实例处理,就可以这样做。我把这个留给你。


#!/usr/bin/perl

use strict;
use warnings;

while (<>) {
   my @fields = split;
   # Using a lookup hash would be faster if you know
   # the specific values C<< $fields[9] >> can take.
   # Something like the following before the loop:
   # C<< my %skip = map { $_ => 1 } qw( X Y Z K ); >>.
   # Then,you'd use C<< !$skip{$fields[9]} >>
   # instead of a regex match.
   if (@fields == 10 && $fields[9] !~ /[XYZK]/) {
      my ($x,$y) = split(/,/,substr($fields[7],1))
      print "$fields[1] $x $y\n";
   } 
   
   if ($. % 10_000_000 == 0) { 
      my $ts = localtime();
      print STDERR "[$ts] prog_a processed $. lines.\n";
   }
}
#!/usr/bin/perl

use strict;
use warnings;

# Should be safe to use since it's deemed safe enough to enabled by default in Perl 7.
# Allows us to cleanly avoid repeatedly doing the same hash lookup.
# We could use a reference instead of an alias by replacing
#    \my $coords = \$H_instBlock_coordinates{$block};
# with
#    my $coords_ref = \$H_instBlock_coordinates{$block};
# and changing all other instances of
#    $coords
# with
#    ${$coords_ref}
# But this would be a lot more noisy.
use experimental qw( refaliasing );

my %h_kI_vB = ...;

my %hierarchical_blocks;
my %H_instBlock_coordinates;
while (<>) {
   my ($this_inst,$x,$y) = split;

   # C<< $this_inst >> contains something like C<< a/b/c >>

   my @loc_xyz;
   {
      my $wip = $this_inst;
      while (1) {
         # If an existing C<< $h_kI_vB{$wip}) >> won't ever be a false value
         # (zero or an empty string),replacing C<< exists($h_kI_vB{$wip}) >>
         # with C<< $h_kI_vB{$wip} >> would be a tiny tiny bit faster.
         if (exists($h_kI_vB{$wip})) {
            push(@loc_xyz,$h_kI_vB{$wip});
         }

         # The regex engine is pretty heavy,so while the
         # remainder of the loop could be replaced with
         # C<< $wip =~ s{/[^/]*\z}{} or last; >>,it
         # probably wouldn't be as fast.
         ( my $i = rindex($wip,"/") ) >= 0
            or last;

         substr($wip,$i,length($wip),"");
      }
   }

   if (@loc_xyz) {
      my $block = join(":",@loc_xyz);
      $hierarchical_blocks{$this_inst} = $block;

      # C<< $block >> contains something like C<< d:e:f >>.
      # It may have fewer parts than C<< $this_inst >> did.

      # C<< $coords >> is an alias for C<< $H_instBlock_coordinates{$block} >>.
      \my $coords = \$H_instBlock_coordinates{$block};
      if ($coords) {
         if    ($x < $coords->[0]) { $coords->[0] = $x; }
         elsif ($x > $coords->[2]) { $coords->[2] = $x; }

         if    ($y < $coords->[1]) { $coords->[1] = $y; }
         elsif ($y > $coords->[3]) { $coords->[3] = $y; }

         print $fh_log "For inst:$this_inst Changing coordinate to block $block: @$coords\n";
      } else {
         $coords = [ $x,$y,$y ];
         print $fh_log "For inst:$this_inst " .                         "$block: @$coords\n";
      }
   }

   if ($. % 1_000_000 == 0) {
      my $ts = localtime();
      print STDERR "[$ts] prog_b processed $. lines.\n";
   }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-