Why does the package qualification of symbols result in less memory used, even if the symbols are imported locally?
Asked Answered
Z

1

6

Note, I tried testing this before in this question which may look similar, however those results were flawed and a result of constant folding, which I subsequently disabled. and republished in this question.


Given these two evals (comment one on execution) which only change &Module::FOO() and &FOO().

# Symbols imported, and used locally.
eval qq[ 
  package Foo$num;
  Module->import();
  my \$result = &Module::FOO() * &Module::FOO();
] or die $@;

# Symbols imported, not used locally referencing parent symbol.
eval qq[ 
  package Foo$num;
  Module->import();
  my \$result = &FOO() * &FOO();
] or die $@;

why would the top block take up substantially less space? The script and output are reproduced below,

Script

package Module {
  use v5.30;
  use warnings;
  use constant FOO => 42; 
  use Exporter 'import';
  our @EXPORT = ('FOO');
}


package main {
  use v5.30;
  use autodie;
  use warnings;

  $|=1;
  say "Our PID is $$";

  for my $num ( 0..1e5 ) { 
    eval qq[ 
      package Foo$num;
      Module->import();
      my \$result = &Module::FOO() * &Module::FOO();
    ] or die $@;
    eval qq[
      package Foo$num;
      Module->import();
      my \$result = &FOO() * &FOO();
    ] or die $@;
  }

  say "DONE";

  _debug();

}

sub _debug {
  open my $fh, "<", "/proc/$$/status";
  while ( <$fh> ) { 
    next unless /Rss/;
    print;
  }
}

Results

Package (namespace) qualified

RssAnon:     101856 kB
RssFile:       5228 kB
RssShmem:         0 kB

Locally imported

RssAnon:     151528 kB
RssFile:       5224 kB
RssShmem:         0 kB

Updates

It was requested by aquanight on irc.freenode.net/#perl to try this experiment with constant directly, here is the code I used,

eval qq[ 
  package Foo$num;
  use constant FOO => 42;
  my \$result = &FOO() * &FOO();
] or die $@; 
eval qq[ 
  package Foo$num;
  use constant FOO => 42;
  my \$result = &Foo${num}::FOO() * &Foo${num}::FOO();
] or die $@;

With this both examples performed as such, to be clear, it was actually worse to use the variant with constant directly and to access the constants with package qualification, then it was to create another package which has the constants to and package-qualify to that package.

Our PID is 204846
RssAnon:     143560 kB
RssFile:       5196 kB
RssShmem:         0 kB
Zaidazailer answered 4/2, 2021 at 2:32 Comment(3)
@ikegami yes, and i probably should write that.Zaidazailer
I usually place the two cases in the then and else of if ($ARGV[0])Threegaited
I'm guessing the call modifies the glob. In one case, the glob in question is *Foo##::FOO. In the other, it's just *Module::FOO. Maybe Dump the glob before and after? No, no changeThreegaited
Z
4

Exporting the entire symbol

It seems that you can get very close if you export the entire symbol rather than one slot in the glob,

our @EXPORT = ('*FOO');

Re-running the same tests, wou will find that both are very similar

RssAnon:      93900 kB
RssFile:       5228 kB
RssShmem:         0 kB

In fact, this is much closer to the theoretical maximum, which cuts out the call to import entirely,

eval qq[
  package Foo$num;
  my \$result = &Module::FOO() * &Module::FOO();
] or die $@;

Which produces,

RssAnon:      74528 kB
RssFile:       5160 kB
RssShmem:         0 kB

Perhaps, maximum black magick joy.

However if we examine this nugget of glory, as suggested by aquanight,

our sub FOO;      # top of file
Module->import();

package Bar;
print FOO();      # works
print Bar::FOO(); # does not work

The symbol FOO is made available to package Bar without bloating the Bar package. When this method is applied to the above benchmark, it looks like,

eval qq[
  our sub FOO ();
  Module->import();
  package Foo$num;
  my \$result = FOO() * FOO();
] or die $@;

And produces these results,

RssAnon:      75112 kB
RssFile:       5284 kB
RssShmem:         0 kB

With constant.pm,

This can be further implemented as,

eval qq[
  our sub FOO ();
  use constant FOO => 42;
  package Foo$num;
  my \$result = FOO() * FOO();
];

Which will produce these results,

RssAnon:      75076 kB
RssFile:       5180 kB
RssShmem:         0 kB
Zaidazailer answered 4/2, 2021 at 4:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.