tags:

views:

122

answers:

3

This sample script:

#!/usr/bin/perl -w

while (1)
{
  sleep(1);
}

takes about 264 kB

grep -A1 heap  /proc/9216/smaps 
0817b000-081bd000 rw-p 0817b000 00:00 0          [heap]
Size:               264 kB

but when I only add my module:

#!/usr/bin/perl -w

use my_module;

while (1)
{
  sleep(1);
}

it takes 18092 kB !

grep -A1 heap  /proc/9219maps 
0817b000-09326000 rw-p 0817b000 00:00 0          [heap]
Size:             18092 kB

Note: The 'my_module' has a lot of 'use module;' inside it.

How can I find what takes so much memory ?

How can I reduce it ? (using 'use module (function)' ?)

Thanks for your help.

+1  A: 

You didn't leave any specifics, but you already answered the general question: Yes, using lots of modules can consume a lot of memory, depending on what those modules are and what they import. Absolutely, you will use less memory if you use fewer modules, or selectively import symbols (or none at all).

Can you provide a more specific example of something that is consuming more memory than you think it should?

Ether
A: 

To answer: How can I reduce it ? (using 'use module (function)' ?)

Instead of a large amount of "use", perhaps just using "require" when you need to use each module within your "my_module". That's what I do to cut back on time/size. That is if you don't need every single module that you're "use"ing in your "my_module".

chris d
But use and require are close to equivalent, although each runs during a different phase: http://perldoc.perl.org/functions/use.html
Greg Bacon
Why does this cut down on size? It just changes when the modules are loaded, not how (unless you don't always execute the `require` statements).
mobrule
I meant for if you don't always execute the "require" statements.
chris d
Also 'require' doesn't import symbols, but one shouldn't be blanket importing lots of symbols anyway. Only import what you need, and scold modules that export everything by default.
Ether
+12  A: 

Insert BEGIN {} blocks to narrow down the culprit as in

#! /usr/bin/perl

sub grep_heap { print @_, "\n"; system "grep -A1 heap /proc/$$/smaps" }

BEGIN { grep_heap "<null>" }
use warnings;
BEGIN { grep_heap "+warnings" }
use strict;
BEGIN { grep_heap "+strict" }

use Data::Dumper;
BEGIN { grep_heap "+Data::Dumper" }
use POSIX;
BEGIN { grep_heap "+POSIX" }

print "Hi\n";

On my Linux host, I see

$ ./prog.pl
<null>
0889b000-088de000 rw-p 0889b000 00:00 0                                  [heap]
Size:               268 kB
+warnings
0889b000-08920000 rw-p 0889b000 00:00 0                                  [heap]
Size:               532 kB
+strict
0889b000-08920000 rw-p 0889b000 00:00 0                                  [heap]
Size:               532 kB
+Data::Dumper
0889b000-089a4000 rw-p 0889b000 00:00 0                                  [heap]
Size:              1060 kB
+POSIX
0889b000-08ace000 rw-p 0889b000 00:00 0                                  [heap]
Size:              2252 kB
Hi

As for what to do about it, you could implement replacement modules with scaled down functionality or ask yourself whether you really need a particular module at all. However, in general, perl's design prefers to throw memory at problems, and these days it's common for machines to have multiple gibibytes of main memory.

Is this resource issue causing performance problems?

Below is a program that reads through the list of modules in perlmodlib.pod and for each module forks a child to require and import it and check its own heap.

#! /usr/bin/perl

sub heap {
  my($heap) = @_;
  unless ($heap =~ /^([0-9a-f]+)-([0-9a-f]+)/m) {
    warn "$0: unexpected heap:\n$heap";
    return -1;
  }
  hex($2) - hex($1);
}

sub size {
  my($bytes) = @_;

  my @units = (
    [ MiB   => "%.1f", 1_024 * 1_024 ],
    [ KiB   => "%.1f", 1_024 ],
  );

  for (@units) {
    my($unit,$fmt,$n) = @$_;
    return sprintf "$fmt %s" => $bytes/$n, $unit
      if $bytes >= $n;
  }

  return "$bytes byte" . ($bytes == 1 ? "" : "s");
}

my %incr;

my $perlmodlib = `perldoc -l perlmodlib`;
die "$0: perldoc failed" unless defined $perlmodlib;

my $base = heap `grep heap /proc/$$/smaps`;
warn "$0: base=" . size($base) . "\n";

chomp $perlmodlib;
open my $fh, "<", $perlmodlib   or die "$0: open $perlmodlib: $!";

while (<$fh>) {
  next unless /^=head2 Pragmatic Modules/ ..
              /^=head2 Extension Modules/;

  if (/^=item (\w+(::\w+)*)/) {
    my $mod = $1;
    (my $path = "$mod.pm") =~ s!::!/!g;

    my $pid = open my $fh, "-|";
    die "$0: fork: $!" unless defined $pid;

    if ($pid == 0) {
      open STDERR, ">", "/dev/null" or warn "$0: open: $!";
      exec "perl", "-e", <<EOProgram;
BEGIN {
  require \"$path\";
  eval { $mod->import };
  system qq(grep heap /proc/\$\$/smaps);
}
EOProgram
      die "$0: exec: $!";
    }
    else {
      local $/;
      my $heap = <$fh>;
      unless (defined $heap && length $heap) {
        warn "$0: use $mod failed";
        next;
      }
      $heap = heap $heap;
      $incr{$mod} = $heap > 0 ? $heap-$base : $heap;
    }
  }
}

foreach my $mod (sort { $incr{$b} <=> $incr{$a} } keys %incr) {
  print "$mod - ", size($incr{$mod}), "\n";
}

The top few offenders for perl-5.8.8:

CPAN::Nox - 9.7 MiB
CPAN - 9.6 MiB
ExtUtils::MM_VMS - 5.3 MiB
CPAN::FirstTime - 5.2 MiB
ExtUtils::Installed - 5.2 MiB
B::CC - 5.2 MiB
bigrat - 4.9 MiB
Math::BigRat - 4.8 MiB
ExtUtils::MM_NW5 - 4.7 MiB
ExtUtils::MM_OS2 - 4.6 MiB
ExtUtils::MM_Win32 - 4.6 MiB
ExtUtils::MM_Win95 - 4.6 MiB
Greg Bacon
Excellent idea ! I'll try that right now ! :)
sebthebert
The first version of your answer was great, the last is awesome ! :)
sebthebert
Thanks! I'm glad to help. So are you gonna let us know which modules caused the trouble for you?
Greg Bacon
Actually, the problem was not a specific module, but just the long list of 'use' in my module... :( I just update my code with more specific 'use' and I already reduced the memory heap usage by 13% !
sebthebert
Something to remember: "Don't abuse of 'use' !" :)
sebthebert