【问题标题】:How to optimize the following subroutine?如何优化以下子程序?
【发布时间】:2013-04-02 05:36:28
【问题描述】:

这是我要优化的子程序。它大部分使用数组引用。目前这个子程序大约需要。平均运行 30-40 秒。如果可能的话,我想将其减少到 10 秒。你有没有看到任何不必要的东西突然出现在你面前?

sub compute{
    # takes two params: 2 array_refs
    my ($gene_exp_ref, $centroids_ref) = @_;
    my ($numerator, $denominator) = 0;

    my ($prod_ref, $diff_x_ref, $diff_y_ref, $x_sq_ref, $y_sq_ref) = [];  # diff_y is the center_gene
    my %gene_center_pcc;                   # diff_x is gene of interest

    my $gene_exp_average = mean($gene_exp_ref);

    for my $gene_exp (@{$gene_exp_ref}) {
        push(@{ $diff_x_ref }, ($gene_exp - $gene_exp_average));
    }

    # possible bottleneck
    for my $centroid_gene_exp_ref (values %{$centroids_ref}){
        $diff_y_ref = [];  # initilize back to empty array
        for my $index (@{$centroid_gene_exp_ref}) {
            push(@{ $diff_y_ref }, ($index - mean($centroid_gene_exp_ref)));
        }

        @{ $prod_ref } = map { @{ $diff_x_ref }[$_] * @{ $diff_y_ref }[$_] } 0..$#{ $diff_x_ref };

        $numerator = sum($prod_ref);

        @{ $x_sq_ref } = map {$_*$_}@$diff_x_ref;
        @{ $y_sq_ref } = map {$_*$_}@$diff_y_ref;

        $denominator = sqrt(sum($x_sq_ref)) * sqrt(sum($y_sq_ref));

        my $r = $numerator/$denominator;

        my ($center) = grep { @{$gene_centers{$_}} ~~ @$centroid_gene_exp_ref } keys %gene_centers;
        $gene_center_pcc{$center} = $r;
    }

#return the center with the highest PCC
return (sort {$gene_center_pcc{$b} <=> $gene_center_pcc{$a}}
    keys %gene_center_pcc)[0];
}

每个计算和数字运算步骤都是必要的。它可以编译,但除非您有数据文件,否则您将无法正确使用子例程。

【问题讨论】:

  • 代码?没有代码很难回答这个问题。
  • 您为什么要编辑问题以完全删除代码?能够查看原始代码是了解推荐优化价值的重要部分。

标签: performance perl optimization hash reference


【解决方案1】:
for my $index (@{$centroid_gene_exp_ref}) {
    push(@{ $diff_y_ref }, ($index - mean($centroid_gene_exp_ref)));
}

这将重新计算@{$centroid_gene_exp_ref} 中每个项目的平均值。如果该数组很大,它将以指数方式累加(我假设mean() 不会缓存或记忆结果,每次调用时都会强制它循环遍历数组)。您可以通过自己缓存均值来节省大量时间:

my $mean = mean($centroid_gene_exp_ref);
for my $index (@{$centroid_gene_exp_ref}) {
    push(@{ $diff_y_ref }, ($index - $mean));
}

除此之外,请与 Devel::NYTProf 联系以查找您的实际瓶颈并在这些点上优化目标。

【讨论】:

  • 太棒了!谢谢,这将运行时间降低到大约。 5- 12 秒。 :-) 你还有什么看到的吗?
【解决方案2】:

您需要从大局出发,考虑到您之前的帖子,其中您表明您为%$centroids_ref 中的每个键调用了compute()

foreach my $key ( keys %HoA ) {
    compute($HoA{$key}, \%HoA);  # on the first iteration, this actually passes an aref to [1,3,3,3]
}

即使经过 Dave Sherohman 的优化,您仍然会一遍又一遍地进行大量计算(例如 mean)。

我的建议是你将外循环带入compute()。然后对于 HoA 中的每个键,您可以存储您的计算并为每个键重用这些值。

sub compute{
    my ($centroids_ref) = @_;

    # precalculate these values once
    my %means;
    my %diffs;
    my %sqrts;
    foreach my $key (keys %$centroids_ref) {
        my $mean = mean($centroids_ref->{$key});
        my @diffs = map {$_ - $mean} @{$centroids_ref->{$key}};

        my @squares = map {$_ * $_} @diffs;
        my $sqrt = sqrt(sum(\@squares));

        $means{$key} = $mean;
        $diffs{$key} = \@diffs;
        $sqrts{$key} = $sqrt;
    }

    # now do the main calculations from the 'possible bottlenecks' section
    ...
}

【讨论】:

  • @steveni 感谢您的建议!我会试试的。您可以删除上面发布的子程序吗?它还没有准备好开源:0
  • 非常感谢。会做!我将在今天晚些时候实施,然后回复您。
  • 我得到的时间大约在。 16-25 秒,它没有按照最初的预期进行。我发布的原始子例程将两个 array_refs 作为参数,但在您的解决方案中,您只使用传入的第二个 array_ref。
  • 是的,子程序已经改变。这意味着您不再需要从循环中调用它,因为循环现在在其中。 (我刚刚意识到我的 return 语句版本有一个错误,应该返回值的散列而不是单个值。)
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
  • 2012-08-21
  • 1970-01-01
相关资源
最近更新 更多