Perl:在各种包中共享子程序实现?

时间:2011-10-24 21:21:20

标签: perl function memory

在这里出现一个愚蠢,不实际的问题。您知道在Perl中,您可以将子例程存储在变量中,例如Javascript和其他语言。子程序会占用一些内存。

现在您可能会认为它会节省内存以使用对子例程的一个引用来为各种包中的许多相同子例程提供实现。像所有那些制定者和吸气者一样。 (是的,我应该使用其中一个现有的mooses或老鼠或诸如此类的东西,但这是一个非实际的问题。)或者像你可能想要在各种包中使用的其他例程一样,以使所有这些包能够做一些横切WTF。

你可以让每个班级的代表来完成这项工作。但也许这太重了太OO。你想要子程序。也许这被称为混合?无论如何,你想在每个包中安装一个子程序,认为它可以节省内存,你可以测试一行,如下所示:

perl -lwe "$s=sub { print 33 };
  for (1 .. 20000) { $p=qq(Bla${_}::bla); *$p = $s} Bla987::bla(); <>"

(好吧,这里有两行来容纳页面宽度。请注意,在Windows上需要双引号,在Linux上你需要使用单引号来避免shell吃掉你的钱。)

现在这段有趣的代码(质量可疑,仅用于演示目的)是创建20,000个包并在每个包中安装该子例程(在相应的符号表条目中)。

根据我创建的软件包数量,我可以看到内存消耗在{{taskmgr}}中上下变化(20,000个软件包为12.2 KB,10,000个软件包为6.7,100个软件包为1.0 KB) - 总而言之),但我想这不是因为副本被复制,而是因为正在创建额外的包。

这是对的吗?子文件没有被复制,它只被引用?

关于这个主题有什么好的阅读?或者这是冒险实现细节和perl5porter的东西吗?

或者这里的整个思路只是误导了沉思和疑惑?

更新

好的,我不确定{{$ s}}中的{{sub}}是否会被重用 - 因为当我在循环中创建新的子程序时,内存使用率不会上升:

perl -lwe "for (1 .. 20000) {
  $p=qq(Bla${_}::bla); *$p = sub {print 33} } Bla987::bla(); <>"

但也许那是因为sub是不变的。

如果我在子中引入变量元素,内存使用率会上升(10,000个包的10 KB,20,000的20 KB),如下所示:

perl -lwe "for my $i (1 .. 10000) {
  $p=qq(Bla${i}::bla); *$p = sub {print $i} } Bla987::bla(); <>"

1 个答案:

答案 0 :(得分:5)

Perl只会编译子程序一次。您正在传递对它的引用,如果您检查地址,它将是相同的。

内存泄漏来自创建20,000个包,这些包是20,000个哈希引用,每个包含一些魔法需要更多的内存。每个哈希引用都包含带有子例程名称的字符串的副本。每个哈希的引用都放在main::符号表中。

如果您希望子程序在所有包中的范围内,请使用完全限定名称,将子例程放入跨越所有包的词法中,或将子例程放入符号变量中:

 sub some::name {}  # call as `some::name()` from anywhere

 my $x = sub {};    # call as `$x->()` anywhere $x is in scope

 *!    = sub {};    # call as `&!()` from anywhere

上述任何一种解决方案都可以让您从其他软件包中访问,而不会浪费大量内存。

根据您的更新,您看到闭包大小增加,因为虽然每个subref相同,但每个都必须创建一个新的词法填充来保存闭包变量,这对每个闭包都是唯一的。

你也可以使用UNIVERSAL包在任何地方使用方法,但这可能会有问题,所以请注意以下几点:

sub UNIVERSAL::foo {say "foo(@_)"}

xyz->foo;  #  foo(xyz)