pperl 0.6.2#

2026-07-16 to 2026-07-18, 35 commits.

Language and runtime#

  • Pragmas built into the binary: strict, warnings, feature, builtin, lib, O and DynaLoader are part of the pperl binary, in the versions that match its interpreter, instead of being read from disk. pperl looked for its own warnings.pm next to the binary, and where it did not find it, no warnings 'experimental::class' could switch off the wrong warning.

  • PPERL5LIB works like PERL5LIB for pperl alone and is searched before it. The trees /usr/local/share/pperl/site_perl, /usr/share/pperl/site_perl and /usr/share/pperl/vendor_perl are searched before the host perl’s library trees, so a module can be installed for pperl without touching the trees perl5 uses. Neither can replace a module built into pperl.

JIT and parallelization#

  • Grids in compiled loops: reads and stores through nested array references, such as $grid->[$y][$x] and $a[$i][$j], compile, at any depth from two to eight levels, and mix with plain array and hash elements in the same loop. A rectangular grid of numbers is copied into a flat buffer once when the loop starts. An output grid that the loop fills from an empty [] is built as perl5 builds it, with undefined rows and short rows left as they are. A Game of Life program runs 10.4 times as fast as with --no-jit.

  • Conditions and loop control in compiled loops: next, and next if and last if with compound conditions such as A || B, A && B or !$a && !$b, compile inside loop bodies, as do foreach loops over integer ranges nested to any depth, the conditional operator ?: used as a value, and && and || over plain values.

  • Nested for my loops: a for my $j (...) loop inside another for my $i (...) loop compiled nothing; such nests now compile. A matrix difference written as $C[$i][$j] = $A->[$i][$j] - $B->[$i][$j] over two nested loops, as in Math::Matrix, runs compiled.

  • Loops the JIT cannot speed up no longer make a program slower than --no-jit. A loop whose compiled runs keep being abandoned, a loop of fewer than 64 iterations that is entered again and again, and a parallel attempt that keeps failing are given up after a few tries instead of paying their set-up cost on every entry. Loops the JIT does speed up are unaffected.

  • Parallel grid loops: a loop that reads one grid and writes each cell of another grid once runs on several cores, with the same output for any thread count. Input grids are shared between the threads instead of being copied for each of them, and the work is cut into fewer pieces when copying the output grid would cost more than the work. A grid map ran 1.51 times as fast on four threads as on one.

  • Parallelization threshold: whether a loop runs in parallel depends on an estimate of its total work, its iterations times the measured cost of its body, instead of a fixed minimum number of iterations. A nested loop of 501 outer iterations runs in parallel, one of 101 stays sequential.

  • blessed in compiled loops: a compiled loop calling Scalar::Util’s blessed leaked memory on every iteration until the system killed the process. 50 million calls used 12.7 GB; they now use 31.6 MB.

Modules#

  • B: B::HV::ARRAY and the B::GV methods FORM, IO and CVGEN exist, so code that walks a package’s symbol table through B works.

  • Digest::SHA: sha256, sha1 and sha512 on short inputs run 1.9 to 3.0 times as fast as on perl5, where they ran at about half its speed, and the object interface 1.96 times as fast.

  • MIME::Base64 and MIME::QuotedPrint: MIME::Base64::encode and decode, and MIME::QuotedPrint::encode and decode, exist as on perl5, as aliases that are not exported. Encoding or decoding a string with characters above 255 dies with “Wide character in subroutine entry”, as on perl5.

  • Storable: freeze of an array runs at 1.05 times perl5’s speed, where it ran at 0.52.

  • Scalar::Util loads List::Util, as it does on perl5. Code that calls List::Util::first { ... } @list with only Scalar::Util loaded, as DBD::File does, died with “Can’t call method first”.

Command line#

  • -M and -m load a module before the program runs, as on perl5, with import arguments as in -Mconstant=ANSWER,42; pperl rejected both switches before. pperl -MO=Deparse prints the program back as source and pperl -MO=Concise prints its op tree.

  • --threads accepts phys (all physical cores), log (all logical cores, hyperthreads included), phys-N and log-N (N fewer, at least one), P% of the logical cores, or a number, which is taken as given even beyond the number of cores. Without --threads, pperl uses all physical cores; it used all logical cores before.

Documentation#

  • Differences from Perl 5: the page is rewritten to list the deliberate differences that remain (no loading of compiled extensions, the module search path, no interpreter threads, Linux only, the version pperl reports) and the current known issues. Limitations that no longer exist are removed from it.