Hack 33. Presolve Module Paths
Make programs on complex installations start more quickly. In certain circumstances, one of Perl's major strengths can be a weakness. Even though you can manipulate where Perl looks for modules (@INC) at runtime according to your needs [Hack #29], and even though you can use thousands of modules from the CPAN, your system has to find and load these modules.
For a
The Hack
"Trace All Used Modules" [Hack #74] shows how
package Devel::Presolve;
use strict;
use warnings;
my @track;
BEGIN { unshift @INC, \&resolve_path }
sub resolve_path
{
my ($code, $module) = @_;
push @track, $module;
return;
}
INIT
{
print "BEGIN\n{\n";
for my $tracked (@track)
{
print "\trequire( \$INC{'$tracked'} = '$INC{$tracked}' );\n";
}
print "}\n1;\n";
exit;
}
1;
Devel::Presolve
's
resolve_path( )
captures every request to load a module, stores the module
Running the Hack
Put
Devel::Presolve
somewhere in your
$ perl -MDevel::Preload slow_program.pl > preload.pm preload.pm will contain something similar to:
BEGIN
{
require( $INC{'CGI.pm'} = '/usr/lib/perl5/5.8.7/CGI.pm' );
require( $INC{'CGI/Util.pm'} = '/usr/lib/perl5/5.8.7/CGI/Util.pm' );
require( $INC{'vars.pm'} = '/usr/lib/perl5/5.8.7/vars.pm' );
require( $INC{'constant.pm'} = '/usr/lib/perl5/5.8.7/constant.pm' );
require( $INC{'overload.pm'} = '/usr/lib/perl5/5.8.7/overload.pm' );
}
1;
You can either include the contents of this file at the start of slow_program.pl or load it as the first module. If you do the latter, put the file in a directory at the front of @INC , lest you erase any performance gains.
Note that the trick of assigning to
%INC
within the
require
avoids a
Hacking the Hack
Pre-resolving paths likely won't help long-running programs. For short-running programs where startup time can dwarf calculation time, it may, depending on how complex your
@INC
is. Be especially careful that upgrading Perl or installing new versions of modules may invalidate this cacheit
is
a cacheand cause
|
Hack 34. Create a Standard Module Toolkit
Curb your
Most
If those documents contain lists of files that you need to manipulate, then you probably use
File::Spec
or
File::Spec::Functions
as well, and perhaps
File::Find
too. Maybe you need to verify and manipulate dates and times on those files, so you regularly pull in half a
If the application has an interactive component, you might continually need to use the prompt( ) subroutine from IO::Prompt [Hack #14]. Likewise, you might frequently make use of the efficient slurp( ) function from File::Slurp . You might also like to have Smart::Comments instantly available [Hack #54] to simplify debugging. Of course, you always specify use strict and use warnings , and probably use Carp as well. A Mess of ModulesThis adds up to a tediously long list of standard modules, most of which you need to load every time you write a new application: #! /usr/bin/perl use strict; use warnings; use Carp; use Smart::Comments; use XML::Parser; use File::Spec; use IO::Prompt qw( prompt ); use File::Spec::Functions; use File::Slurp qw( slurp ); use DateTime; use DateTime::Duration; use DateTime::TimeZone; use DateTime::TimeZone::Antarctica::Mawson; # etc. # etc.
It would be great if you could shove all these usual
package Std::Modules; use strict; use warnings; use Carp; use Smart::Comments; use XML::Parser; use File::Spec; use IO::Prompt qw( prompt ); use File::Spec::Functions; use File::Slurp qw( slurp ); use DateTime; use DateTime::Duration; use DateTime::TimeZone; use DateTime::TimeZone::Antarctica::Mawson; # etc. 1; and just use that one module instead: #! /usr/bin/perl use Std::Modules;
Of course, that fails dismally. Using a module that uses other modules isn't the same as using those other modules directly. In most cases, you'd be importing the
The Hack
What you really need is a way to create a far more cunning module: one that cuts-and-pastes any
use
statements inside it into any file that uses the module. The
package Std::Modules; use Filter::Macro; # <-- The magic happens here use strict; use warnings; use Carp; use Smart::Comments; use XML::Parser; use File::Spec; use IO::Prompt qw( prompt ); use File::Spec::Functions; use File::Slurp qw( slurp ); use DateTime; use DateTime::Duration; use DateTime::TimeZone; use DateTime::TimeZone::Antarctica::Mawson; # etc. # etc. 1; Now, whenever you write: #! /usr/bin/perl use Std::Modules; all of those other use statements inside Std::Modules are pasted into your code, in place of the use Std::Modules statement itself. Hacking the HackThere's also a more modular and powerful variation on this idea available. The Toolkit module (also on CPAN) allows you to specify a collection of standard module inclusions as separate files in a standard directory structure. Once you have them set up, you can automatically use them all just by writing: #! /usr/bin/perl use Toolkit; The advantage of this approach is that you can also set up "conditional usages"files that tell Toolkit to import specific subroutines from specific modules, but only when something actually uses those subroutines. For example, you can tell Toolkit not to always load: use IO::Prompt qw( prompt ); use File::Slurp qw( slurp );
but only to load the
IO::Prompt
module if something actually uses the
prompt( )
subroutine, and likewise to
That way, you can safely specify dozens of handy subroutines and modules in your standard toolkit, but only pay the loading costs for those you actually use. |