Perl创建对象的速度非常慢

时间:2015-05-31 03:35:25

标签: performance perl object

我有一个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中的重复。问题开始于评论下面的循环中,“这是问题开始的地方”。我在循环中添加了一条消息,为每100个创建的对象记录一行。前100个对象是在5秒内创建的。接下来的100分钟需要16秒。达到300需要30多秒。这是多达9000个对象,需要12分钟以上才能创建100个对象。我认为50,000个物体的大小不足以产生这些问题。

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

我在Windows笔记本电脑上运行ActiveState Perl 5.16,内存为8 GB,i7处理器(3年前)和具有合理空间的SSD驱动器。我在具有相同版本Perl的Linux机器上看到过这个,所以我不认为这是硬件。我需要留在AS Perl的5.16。任何有关如何提高性能的建议都将受到赞赏。感谢。

2 个答案:

答案 0 :(得分:5)

首先:描述你的程序!你已经把它缩小到一个子,用Devel::NYTProf(例如)你可以将它缩小到罪魁祸首这一行

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

只是瞥一眼它,一些可能的减速因素立即浮现在脑海中,但你不能确定你是否

Mayhe散列分配花费的时间太长。随着%objects哈希的增长,perl将稳定地分配更多内存。 您可以预先设置$objects哈希的大小。此功能为documented here。由于这是一个内存分配问题,如果您使用太小的数据集配置文件,则无法识别这一点。

# 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"是假的,因为例子)。

但同样,最重要的是:描述您的程序。也许采用较小的数据集,但您无法清楚地看到内存分配问题。但是,通过分析,您将看到,此时您的程序占用的时间最多。

The perldoc has something to say about speeding up your program。它也有关于分析的很好的章节。

答案 1 :(得分:1)

正如您所读到的,在进行优化之前,必须使用分析器来确定代码中的瓶颈所在。但是,正如我在评论中所描述的那样,可以不同地重写循环,以便不会不必要地创建和丢弃未使用的哈希

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

的改进

以下是对代码的修改,可以为您提供一些想法

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;
}