【问题标题】:Passing large values to modules将大值传递给模块
【发布时间】:2021-02-19 12:40:17
【问题描述】:

我的程序运行缓慢,并且正在尝试提高性能。脚本“使用”模块中的一个子程序,并将一个非常大的数组传递给子程序。经过一番修改,我意识到如果我将子直接移动到父脚本中,并使数组全局而不是本地(所以我不必传递它),脚本会大大加快(在几分钟内运行它正在占用天)。

我真的很希望能够在模块中包含那个子(因为我有很多脚本调用同一个子)。但我也希望它快点。 :-)

半伪代码

page.pl:

package Page;

use Star;
my @fileBytes=();
open(StarFile, "<$File");
binmode(StarFile);
while (read(StarFile, $FileValues, 1)) {
  push @fileBytes, $FileValues; 
}
close(StarFile);

&parseBlock(\@fileBytes);

模块.pl:

package Star;

sub parseBlock {
  my ($fileBytes) = @_;
  my @fileBytes = @{ $fileBytes };

  ...
}

这里有一些阅读:https://www.perlmonks.org/?node=Variable%20Scoping%20in%20Perl%3A%20the%20basics 告诉我我想处理范围界定。所以如果我用“我们的”而不是“我的”来定义@fileBytes,它就会变成一个包值。据我所知,这通常在模块文件中。但我从父级中的值开始。

所以我可以让父级也成为一个包,定义: 我们的@fileBytes

然后从模块中引用它,至少类似于: @Page::fileBytes

我认为我至少在理论上是正确的。

当我想从不同的脚本中使用 sub 时,我的问题出现了:

其他.pl:

package Other;
use Star;

  my @fileBytes=();
  open(StarFile, "<$File");
  binmode(StarFile);
  while (read(StarFile, $FileValues, 1)) {
    push @fileBytes, $FileValues; 
  }
  close(StarFile);

&parseBlock(\@fileBytes, $offset);

现在我传递的值是 @Other::fileBytes 。这个问题随着我使用我的图书馆的次数越多而扩大。

我想做的是在模块中有子例程,但不必传递(我相信这是创建一个新值,这一定很慢)@fileBytes 数据,因为它是“全局的” , 这样我就可以使用集中式子了。

【问题讨论】:

  • 这与范围无关。您将数组引用传递给parseBlock(),这很好,因为它允许子例程直接访问此数据。在您的 subs 第二行中,您复制了可能不必要的数组内容,并且在大量数据不幸的情况下,但不太可能考虑您描述的性能差异(分钟与天)。见perlreftut
  • 一个简单而合理的做法是在 sub 中创建一个本地数组 my @fileBytes = @{ $fileBytes }; --- 而是使用 $fileBytes arrayref(除非该数组在潜艇中得到改变!)。当然,如果数组很大,则该数据复制需要时间。不过,我仍然不明白这是如何将几分钟变成几天的,除非 sub 被多次调用......
  • 我建议不要修改our ...您需要一个获取所需数据的子程序。如果数据大小存在合理问题,那么您需要重新考虑方法,而不是开始将事情全局化。

标签: arrays perl module subroutine


【解决方案1】:

您不能将数组传递给 subs,只能传递标量。当一个使用f(@a) 时,一个是传递数组的元素。这不会创建任何新标量或复制任何标量,因此它实际上仍然非常快。

但是,即使是那么小的成本也可以避免。这是通过传递对数组的引用来完成的:f(\@a)。这确实创建了一个标量,但它是所有标量中最轻的。

这是你已经在做的事情,所以从调用 sub 的角度来看,你已经是最快的了。您面临的问题是您在调用 sub 后立即执行的操作的结果:您创建一个新数组并将提供的数组的每个元素复制到这个新数组中。

my @fileBytes = @{ $fileBytes };  # Copies every element.

删除该行,您的问题就解决了。当然,您需要将使用重复数组 (@fileBytes) 的任何代码更改为使用原始数组 (@$fileBytes)。唯一需要注意的是,对数组的任何更改都将反映在通过引用传递给 sub 的数组中,因为它是同一个数组。


替代解决方案

如果您坚持避免使用引用,则可以使用以下方法:

use experimental qw( declared_refs );

my \@fileBytes = $fileBytes;

有效地使@fileBytes 成为@$fileBytes 的别名。不涉及复制。它不是免费的,但也不昂贵(O(1))。和直接修改@$filesBytes一样,修改@fileBytes会影响调用者中的数组。

一般来说,应该避免在生产环境中使用实验性代码,但开发人员计划在 Perl 的下一个主要版本中默认启用它,因此他们肯定认为它相当稳定。

【讨论】:

    猜你喜欢
    • 2014-08-27
    • 2017-12-01
    • 1970-01-01
    • 2013-07-29
    • 2017-08-07
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    • 2020-02-14
    相关资源
    最近更新 更多