PERL_PERFORMANCE(1) | Makepp | PERL_PERFORMANCE(1) |
makepp_perl_performance -- How to make Perl faster
The biggest tuning gains will usually come from algorithmic improvements. But while these can be hard to find, there is also a lot you can do mechanically.
Makepp is a big heavy-duty program, where speed is a must. A lot of effort has been put into optimizing it. This documents some general things we have found. Currently the concrete tests leading to these results have mostly been discarded, but I plan to gradually add them.
If you are looking at how to speedup makepp (beyond the Perl programming you put into your makefiles), look at makepp_speedup. This page is completely independent of makepp, only intended to make our results available to the Perl community. Some of these measures are common sence, but you sometimes forget them. Others need measuring to believe them, so:
This tells you which functions are the most promising candidates for tuning. It also gives you a hint where your algorithm might be wrong, either within surprisingly expensive functions, or through surprisingly frequent calls.
perl -Mstrict -MBenchmark -we 'my <initialization>; timethis -10, sub { <code> }' time perl -Mstrict -we 'my <initialization>; for( 0..999_999 ) { <code> }'
when run on different variants of code you can think of, can give surprising results. Even small modifications can matter a lot. Be careful not to "measure" code that can get optimized away, because you discard the result, or because it depends on constants.
Depending on your system, this will tell you in kb how fat Perl got:
perl -Mstrict -we '<build huge data>; system "ps -ovsz $$"'
Below we only show the code within the "-e" option as one liners.
$o->method( ... ); # searched in class of $o and its @ISA Class::method( $o, ... ); # static function, new stack Class::method $o, ...; # static function, new stack, checked at compile time &Class::method; # static function, reuse stack
This last form always possible if method (or normal function) takes no arguments. If it does take arguments, watch out that you don't inadvertently supply any optional ones! If you use this form a lot, it is best to keep track of the minimum and maximum number of arguments each function can take. Reusing a stack with extra arguments is no problem, they'll get ignored.
my $self = shift;
Unless you have a pertinent reason for this, use this:
my( $self, $x, $y, @z ) = @_;
If you have a function only called in one place, and the two combined would still be reasonably short, merge them with due comments.
Don't have one function only call another with the same arguments. Alias it instead:
*alias = \&function;
perl -MBenchmark -we 'timethis -10, sub { print STDERR $_ for 1..5 }' 2>/dev/null perl -MBenchmark -we 'timethis -10, sub { print STDERR 1..5 }' 2>/dev/null perl -MBenchmark -we 'timethis -10, sub { my $str = ""; $str .= $_ for 1..5; print STDERR $str }' 2>/dev/null
my $i = 0; our %a = map +($i++, $_), "a".."j"; timethis -10, sub { $b = $a{int rand 10} } our @a = "a".."j"; timethis -10, sub { $b = $a[rand 10] } my $i = 0; my %a = map +($i++, $_), "a".."j"; timethis -10, sub { $b = $a{int rand 10} } my @a = "a".."j"; timethis -10, sub { $b = $a[rand 10] }
my @list = map { bless { $_ => 1 }, "someclass" } 0..9; my( %a, %b ); timethis -10, sub { $a{$_} = 1 for @list }; timethis -10, sub { $b{int()} = 1 for @list }; timethis -10, sub { $b{sprintf '%x', $_} = 1 for @list }; timethis -10, sub { $b{refaddr $_} = 1 for @list };
There is also sprintf '%p' which supposedly outputs a pointer, but depending on which expression leads to the same ref, you get different values, so it's useless.
Separate boolean hash members are faster than stuffing everything into an integer with bit operations or into a string with "vec".
my %x; $x{$_} = 0 for 0..999_999; system "ps -ovsz $$" my %x; undef $x{$_} for 0..999_999; system "ps -ovsz $$" my @x = (0) x 999_999; system "ps -ovsz $$" my @x = (undef) x 999_999; system "ps -ovsz $$"
my @l = 0..99; for( 0..99_999 ) { map $a = " $_ ", @l } for( 0..99_999 ) { map $a = " $_ ", 0..99 } for( 0..99_999 ) { $a = " $_ " for @l } for( 0..99_999 ) { $a = " $_ " for 0..99 }
my $x = "abcdefg"; my $b = 0; for( "$x" ) { $b = 1 - $b if /g/ } # Copy needed only if modifying. for( $x ) { $b = 1 - $b if /g/ } local *_ = \$x; $b = 1 - $b if /g/; local $_ = $x; $b = 1 - $b if /g/; # Copy cheaper than alias. my $y = $x; $b = 1 - $b if $y =~ /g/;
Daniel Pfeiffer <occitan@esperanto.org>
2021-01-06 | perl v5.32.0 |