【问题标题】:Efficient way to loop through 2D hash in perl在 perl 中循环遍历 2D 哈希的有效方法
【发布时间】:2020-09-02 19:18:05
【问题描述】:

我正在寻找一种方法来加快我的 perl 执行速度。 我有一个脚本可以读取文件并创建一个 2D 散列(至少有 800 万个键值对)。然后我创建两个单独的哈希,其中包含基于用户输入的二维哈希的第一级和第二级键。但是当我尝试结合所有这些来打印数据时,执行速度非常慢。 这是打印数据的代码块(脚本中最耗时的部分):

open(my $FH_DATA, ">", $report_graph) or die "Cannot open file $!"; 
print $FH_DATA "$HEADER_GRAPH\n";
  foreach my $first_key ( keys %first_level_hash) {
    foreach my $second_key (keys %second_level_hash ) {
      foreach my $rail (@FILTER_BY_RAILS) {
        if( exists $FILTER_BY_RAIL_COMMON{"$first_key.$second_key.$rail"} ) {
          print $FH_DATA " $_ " for @{ $my_2D_hash{$first_key}{$second_key} };
          print $FH_DATA "$rail $second_key $first_key";
          print $FH_DATA "\n";
        }
      }
    }
  }
close($FH_DATA);
print "Finished writing $report_graph\n";

这个嵌套的 foreach 循环真的浪费了我的执行时间。我一直在看它,现在变得盲目了。非常感谢任何帮助。

NYTProfiler 的输出:

【问题讨论】:

  • 输出文件中的顺序重要吗?如果不是,将最外面的两个循环放在 if 语句中会更有意义。 (for my $rail (...) { if (exists(...)) { for my $first_key (...) { for my $second_key (...) { ... }}}})
  • 刚刚发生在我身上...$cell_key$pin_key 实际上是哈希键?
  • 很抱歉打错字了。 $pin_key 和 $cell_key 实际上是 $first_key 和 $second 键。我将编辑问题。
  • 您是否尝试过使用each 来迭代huge 哈希? while (my ($key, $value) = each %hash) { ... }
  • %FILTER_BY_RAIL_COMMON 中有多少键?

标签: perl optimization hash


【解决方案1】:

更新 作为问题中关键细节的重大重写已更新


代码取消引用两个嵌套的哈希键,以便为每个$rail-filtered 条目准备相同的内容。那些涉及取消引用的哈希查找并不是完全免费的,而且会累加。由于打印的哈希相关部分对于每个过滤器循环都是相同的,因此请在外面准备它们

foreach my $first_key ( keys %first_level_hash) {
    foreach my $second_key (keys %second_level_hash ) {
        my @line_elems = @{ $my_2D_hash{$first_key}{$second_key} };
        foreach my $rail (
            grep { exists $FILTER_BY_RAIL_COMMON{"$first_key.$second_key.$_"} }
            @FILTER_BY_RAILS)
        {
            print $FH_DATA " $_ " for @line_elems;
            print $FH_DATA "$rail $second_key $first_key";
            print $FH_DATA "\n";
        }
}

grep 也应该比显式循环中的 if 语句更有效。

总而言之,这肯定有帮助,但如果 @FILTER_BY_RAILS 很小,那么可能不会太大。

这是关于在不重新安排操作的情况下可以做什么。一个主要的改进是在之前进行过滤,也许在填充散列时创建一个单独的数据结构准备打印。 (然后循环也可以更有效地解开。)

它仍然会花费一些;散列并不快,而且对大散列进行迭代需要周期。


在打印本身方面也可以尝试更快的一个小调整

print $FH_DATA ' '.join('  ', @$line).' ';

【讨论】:

  • 在线my @line_elems = @{ $my_2D_hash{$first_key}{$second_key} }; 我得到:Can't use an undefined value as an ARRAY reference at。数据按照以前的实现方法存在。为什么现在抱怨?
  • @tt_pre 这意味着$h{k1}{k2} 什么都没有……关于名称(哈希和键)?该行是从您的代码中复制的,刚刚移出 @FILTER 循环 - 所以无论您实际拥有什么,将其移出循环并且它必须工作?除非它实际上与此处显示的显着不同并且它使用$rail 元素?或者您还进行了其他更改?
猜你喜欢
  • 2016-08-20
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
相关资源
最近更新 更多