Perl创建对象的速度非常慢



我有一个perl脚本,它从数据库读取约50,000行,并将它们存储在一个散列数组中。标准DBI代码。与其直接处理哈希,我更喜欢将数据放入对象中,这样我就可以非常清晰地传递给其他代码模块。我正在阅读的表中有15+列。我的代码基本上是这样的:

my $db = DBI->connect(); # Just pretend you see a proper DBI connect here
my $resultSet = $db->selectall_arrayref($sql);
$db->disconnect();
# Here's where the problem starts.
my %objects;
for my $row (@{$resultSet}) {
    my ($col1, $col2, ..., $col15) = @{$row};
    my %inputHash;
    $inputHash{col1} = $col1 if $col1;
    ...
    $inputHash{col15} = $col1 if $col15;
    my $obj = Model::Object->new(%inputHash);
    $objects{$col1} = $obj;
}
return values %objects;

它将内容收集到散列中以消除select中的dup。问题开始于注释下面的循环,上面写着"这里是问题开始的地方"。我在循环中放置了一条消息,为每创建100个对象记录一行。前100个对象在5秒内创建完成。接下来的100个花了16秒。到300多花了30秒。它最多有9000个对象,创建100个对象需要12分钟以上。我不认为5万个对象足够大,足以产生这些问题。

正在创建的Model::Object是一个类,每个属性都有getter和setter。它有一个新方法和一个序列化方法(本质上是一个toString),仅此而已。这没有逻辑。

我在一台Windows笔记本电脑上运行ActiveState Perl 5.16,具有8gb RAM, i7处理器(3年)和具有合理空间的SSD驱动器。我在使用相同版本Perl的Linux机器上看到过这种情况,所以我不认为这是硬件问题。我需要留在5.16的AS Perl。任何关于如何提高性能的建议都将不胜感激。谢谢。

首先:配置您的程序!您已经将其缩小到一个子,使用Devel::NYTProf(例如)您可以将其缩小到罪魁祸首行。

以下是我的一些一般性考虑:

只是浏览一下,一些可能的减速因素立即浮现在脑海中,但是如果您没有配置您的程序:

,您就无法确定。

可能哈希分配耗时太长。随着%objects散列的增长,perl将稳定地分配更多内存。您可以预先设置$objects散列的大小。这里记录了这个特性。由于这是一个内存分配问题,如果使用过小的数据集配置,您将无法识别这一点。

# somewhere outside of the loop
keys(%objects) = $number_of_rows * 1.2;
# the hash should be a little bigger than the objects to be stored in it

其次,可能是对象创建时间太长。看看Model::Object。我不知道里面有什么,所以我不能对此发表评论。但最肯定的是,您应该考虑传递%inputHash作为参考。对于Model::Object->new(%inputHash);,您将键和值放在堆栈上,然后检索它,在最坏的情况下为my %options = @_;。这样,您就可以为每个键重新计算哈希值。

也许你可以想出一个方法来完全摆脱小$inputHash。我很快就能想出一些方法,这将基于defined nes,但你正在检查真实性(你确定这是对的,顺便说一句?例如,"0"为false)。

但是,最重要的是:配置您的程序。可能采用较小的数据集,但您无法清楚地看到内存分配问题。但是使用分析,您将确切地看到,在哪个点您的程序花费了最多的时间。

The perldoc有一些关于加速你的程序的话要说。它也有一个关于剖析的很好的章节。

正如您所读到的,在您进一步优化之前,您必须使用分析器来确定代码中的瓶颈在哪里。然而,正如我在评论中所描述的,可以用不同的方式重写循环,这样就不会不必要地创建和丢弃未使用的哈希值

您还应该看到通过引用传递哈希而不是简单的键和值列表的改进

这是你的代码的修改,应该会给你一些想法

use constant COLUMN_NAMES => [ qw/
  col1  col2  col3  col4  col5
  col6  col7  col8  col9  col10
  col11 col12 col13 col14 col15 
/ ];
sub object_results {
    my $dbh = DBI->connect($dsn, $user, $pass);
    my $result_set = $dbh->selectall_arrayref($sql);
    $dbh->disconnect;
    my %objects;
    for ( my $i = $#$result_set; $i >= 0; --$i ) {
        my $row = $result_set->[$i];
        next if exists $objects{$row->[0]};
        my %input_hash;
        for my $i ( 0 .. $#$row ) {
          my $v = $row->[$i];
          next unless defined $v;
          $input_hash{COLUMN_NAMES->[$i]} = $v;
        }
        $objects{$input_hash{col1}} = Model::Object->new(%input_hash);
    }
    values %objects;
}

最新更新