问题描述
我的程序运行缓慢,并且试图提高性能。脚本“使用”模块中的一个子对象,然后将相当大的数组传递给该子对象。经过一番修补后,我意识到如果将子直接移到父脚本中,并使数组成为全局数组而不是局部数组(因此我不必传递它),则脚本的运行速度大大提高(运行时间仅需几分钟)天)。
我真的很希望能够在模块中包含该子对象(因为我有许多脚本调用了该子对象)。但我也希望它快。 :-)
半伪代码
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);
Module.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时出现我的问题:
other.pl:
package Other;
use Star;
my @fileBytes=();
open(StarFile,"<$File");
binmode(StarFile);
while (read(StarFile,1)) {
push @fileBytes,$FileValues;
}
close(StarFile);
&parseBlock(\@fileBytes,$offset);
现在我传递的值是@Other :: fileBytes。当我使用图书馆时,这个问题就扩大了。
我想做的是在模块中有子例程,但不必传递@fileBytes数据(我相信它正在创建一个新值,该值必须很慢),因为它是“全局”的,以使我可以使用集中式子控件。
解决方法
您不能将数组传递给sub,只能传递标量。当使用f(@a)
时,将传递数组的元素。这不会创建任何新的标量或复制任何标量,因此实际上它的速度非常快。
但是,即使很小的成本也可以避免。这是通过将引用传递给数组f(\@a)
来完成的。这确实创建了一个标量,但这是所有标量中最轻的。
这是您已经在做的,因此从调用子程序的角度来看,您已经拥有最快的速度。您面临的问题是在调用sub之后立即执行操作的结果:创建一个新数组并将所提供数组的每个元素复制到该新数组中。
my @fileBytes = @{ $fileBytes }; # Copies every element.
删除该行,您的问题已解决。当然,您需要将使用重复数组(@fileBytes
)的所有代码更改为使用原始数组(@$fileBytes
)。唯一的警告是,对数组的任何更改都将反映在通过引用传递给子对象的数组中,因为它是同一数组。
替代解决方案
如果您坚持要避免使用引用,则可以使用以下内容:
use experimental qw( declared_refs );
my \@fileBytes = $fileBytes;
有效地使@fileBytes
成为@$fileBytes
的别名。不涉及复制。它不是免费的,但也不便宜(O(1))。就像直接修改@$filesBytes
一样,修改@fileBytes
也会影响调用方中的数组。
通常来说,应该避免在生产中使用实验代码,但是开发人员计划在Perl的下一个主要版本中默认启用它,因此他们肯定认为它相当稳定。