pperl 0.6.21#
2026-09-28 to 2026-10-06, 271 commits.
Language and runtime#
Unicode 17 reaches
\N{...},charnames::viacodeand Unicode::UCD: they are perl 5.44’s own, built into pperl, where pperl had read them from the host perl (5.42, Unicode 16) and did not know Unicode 17’s characters. A program using one\N{NAME}no longer spends most of its run compiling the 1.2 MB character-name table (34 ms against a 4 ms baseline); the table is stored in the binary.glob and
<*>match files with perl5’s own matcher instead of the C library’sglob: brace alternatives come back in the order written,GLOB_NOCASEno longer sorts by case,^inside[...],\*and an empty{}mean what they mean in perl5. A directory error stops a glob as in perl5, andFile::Glob::GLOB_ERRORreports the last glob’s error code instead of always 0.
JIT and parallelization#
Parallel loops: compiled loops run on all cores. A counted
foreach,whileor C-styleforloop whose body only computes - integer sums, bitwise reductions, sums of floats whose terms are whole numbers, reads of a hash with integer keys - is split into chunks whose result is bit for bit the sequential one, and a chunk that reaches a call or an exit resumes sequentially at its first iteration, so perl dies or warns where it would. On 4 physical cores a 400-million-trip addition loop takes 0.13 s instead of 0.58 s, and the Benchmarks Game’s mandelbrot written as a loop takes 4.13-4.26 s at N=16,000 instead of 10.95 s. Loops too short to repay the split stay sequential, and--no-parallelturns splitting off.Floating-point loops over arrays, also arrays reached through references, no longer leave compiled code at every float element: a matrix-vector product takes 9.5 ns per multiply-add instead of 248 (79 ns without the JIT), a 4096-element dot product 9.4 ns instead of 70. A
return \@yelsewhere in the sub, or a?:that picks an array element ($x[$i] > 0 ? $x[$i] : 0.0), no longer keeps a loop interpreted; a small neural network trains at 16,000 samples per second with the JIT against 2,500 without, where it had run slower with the JIT (1,800 against 4,000). A float loop such as$sum += ($x > $y)no longer moves its sum through memory on every trip: 5.2 cycles per trip instead of 10.1.Regular expressions compiled by ReJIT: a successful match of a pattern without capture groups records its result itself instead of running the interpreter’s matcher over the match again, and the Benchmarks Game’s regex-redux (single-threaded, 5M) runs 32% fewer instructions and cycles; each compiled pattern reaching its state through its own engine saved a further 3% there. Scans for a literal probe 64 bytes per step and hand long stretches without a candidate to the C library’s
memchr:/aab./over 64K ofawent from 0.14 to 1.02 times perl5’s speed, and the other literal and character-class scans measured run 1.7 to 1.9 times as fast.String operations in compiled loops: 4-argument and lvalue
substrreplacement,vecread and assigned, thexrepetition of a string andjoinover scalars compile, and so do string results stored into package variables and into array or hash elements, and truth tests of strings. Asubstrloop over a 4 MB string runs in 1.49 s instead of 3.65 s (perl5 4.59 s), avecbit sieve in 0.56 s instead of 3.75 s (perl5 4.41 s). Loops that rebuild strings and then test or store them, such as$s += length $gover a package variable or$w = $i & 1 ? "a" : "", no longer fall back to the interpreter on every trip.Array references in compiled loops: element reads, stores,
++and+=through$r->[...],scalar(@$r),$#$r, andpush,unshift,popandshiftthrough a reference compile, as doshift @aand a sub’s implicitshiftof@_. Compiled code now honours a tied scalar holding the reference and a class that overloads@{}, which it had bypassed, and a loop that reads an array through a reference while updating it by name sees its own updates. An object removed bypopin a compiled loop is destroyed at the end of its statement, as in perl, not after the next one.Hashes, pushes and new arrays in compiled loops: a plain hash element is looked up inline, an integer key without first being turned into a string, and the Benchmarks Game’s k-nucleotide at 25M runs 19.2% fewer instructions. A short-lived array built in a loop (
my $v = [$i, $i + 1]) compiles, and one that never leaves the loop is kept in registers: such a loop runs 20.3 times as fast. A loop that pushes computed values onto an array, such as a sieve’s prime extraction, runs 2.93 times as fast.List
foreachloops such asfor my $v (@a, @b),for ($x, $y)andfor (1, 5, 9)compile, also nested in another compiled loop. A benchmark of such loops over two arrays runs in 0.276 s instead of 0.715 s (perl5 0.711 s).Loop conditions that combine comparisons with
&&,||or?:, andwhile ($x)oruntil ($done)on a plain value, compile; such conditions had kept 133 loops in 14 of 21 real programs interpreted. A sub whose last statement is a compiledwhileloop no longer returns a wrong value in list context.More loops compile.
defined $vis tested when the loop runs instead of assumed when it compiles, andref $xas a condition,<=>underuse integer, a C-styleforthat stores into array elements (for (my $k = 0; $k < $n; $k += $q) { $a[$k] = 1 }) and a call to a sub defined further down the file no longer keep a loop interpreted.Fewer compiled loops give up. Loops that compiled but left compiled code at every entry until pperl stopped trying now stay compiled: a
foreachover constants, aforeach my $q (@q), whose loop variable is a value the array holds as well, a copy of a string or of undef into amy,$b[$i] += 1over whole-number floats from a division, and amy $last = ''written on a rare branch. A modifierifas a loop’s last statement no longer grows perl’s stack: a 10-million-trip loop that took 27.1 MB against the interpreter’s 10 MB takes 14.6 MB and 227.8M instructions instead of 507.8M. A loop that keeps leaving compiled code for any other reason is given up after about 4,096 entries instead of retried forever.JIT overhead: a program that compiles nothing pays less for the JIT; on a module-loading test file the cost over
--no-jitfell from 32.5M to 14.5M instructions. Ans///with an expression replacement is no longer taken for a loop to compile, which had made one module’s test file 61% slower with the JIT (now 4.3%), and shortforeachloops over lists and arrays are no longer compiled when they cannot repay the compile.Integers stay integers. Values written by compiled loops keep the integer or floating-point form perl gives them: an integral result stored into an array element, by
+=, or computed throughint()stays an integer where it had become a float, as Devel::Peek and B showed, and later integer reads of it stay compiled. A loop that pperl tried and failed to compile no longer turns an integer variable into a float, which had left 14 string-function benchmarks about 10% slower with the JIT than without.Compiled stores no longer run a tied variable’s
STOREor an object’sDESTROYin the middle of compiled code: a store to a tied destination could leave a loop reading a reallocated array or crash it, and an object whose last reference was overwritten was destroyed when the loop ended instead of at the next statement. A lexical written both by a loop and by subs it calls, such as a variable a closure captures, keeps every write.Memoize: the cache compiled code uses for a memoized function of numbers no longer makes small integer arguments evict each other. A memoized
fibover the arguments 0 to 31 made 1.25M avoidable calls in 20M; its loop now also runs in parallel.
Modules#
The perl 5.44 core library ships inside pperl: every
.pmand.plfile perl 5.44 installs is embedded, 733 more than before, and pperl no longer takes core modules from the host perl. On a host without perl the previous binary could not load Encode, File::Temp, Test::More, Math::BigInt, JSON::PP, Unicode::UCD or autodie, and on a host with perl 5.42 it loaded 5.42’s versions.threadsandthreads::sharedship their 5.44 modules too.Net::SSLeay and IO::Socket::SSL: Net::SSLeay 1.96 is native in the full build, over OpenSSL 3 loaded at run time, with the 709 functions its distribution provides for that OpenSSL; a function the loaded library lacks stays undefined, as in a perl5 build against that library, so feature tests such as
defined &Net::SSLeay::Xanswer correctly. IO::Socket::SSL 2.099 ships beside it. Both distributions’ test suites, and AnyEvent’s including its TLS test, match perl 5.44 running the original modules.DB_File, GDBM_File, NDBM_File and ODBM_File are native and keep their databases in LMDB files, through liblmdb loaded at run time, instead of Berkeley DB or gdbm; DB_File supports Hash, Btree and Recno, and a gdbm or Berkeley DB file is recognised and refused, never written. GDBM_File writes and reads gdbm’s ASCII and binary dump formats, so gdbm 1.26 loads pperl’s dumps and pperl loads gdbm’s, which is how existing gdbm files move over. By default writes are held in one transaction until
sync,untieorclose;$Peta::LMDB::DBM_WRITE = 'through', or GDBM_File’s sync mode, commits each write, and a crash loses writes but never the file.Peta::LMDB is new: LMDB’s environments, transactions, databases and cursors for Perl, with key comparators written in Perl and errors raised as
Peta::LMDB::Errorobjects. The DBM modules above are built on it.LMDB_File 0.14 is native over the same library, and its distribution’s tests match perl 5.44 with the original module. Closing an environment explicitly, closing it twice, or using a transaction of a closed environment no longer crashes; perl5 with the original module aborts or segfaults there.
Class::XSAccessor 1.19 is native, including the faster call path it gives its accessors. Moo uses it for attribute accessors; before, Moo found the host perl’s build, could not load it, and warned “Class::XSAccessor exists but failed to load” before falling back to pure Perl.
XML::Parser 2.59 is native in the full build, over libexpat 2.6.0 or newer loaded at run time, so XML::Dumper and the other modules built on XML::Parser work. Its distribution’s tests match perl 5.44 with the original module built against the same libexpat.
Unicode::LineBreak and HTML::Parser are native. Unicode::LineBreak 2019.001, with Unicode::GCString and Text::LineFold, breaks lines and grapheme clusters by the Unicode 17 rules and passes Unicode 17’s LineBreakTest and GraphemeBreakTest; Thai, Lao, Khmer and Myanmar text is segmented through the system’s ICU. HTML::Parser 3.86 matches perl5 with the original module on its whole test suite.
Unicode::Normalize 1.32 is native and passes its distribution’s tests and Unicode 17’s NormalizationTest, 400,680 cases.
Devel::Size 0.87 is native, and its distribution’s tests match perl 5.44 with the original module.
File::DosGlob, Sys::Syslog and Math::BigInt::FastCalc are native: File::DosGlob 1.12, Sys::Syslog 0.36 and Math::BigInt::FastCalc 0.5020.
PDL loads as on perl5:
PDL.pm, PDL::Lite and PDL::LiteF are upstream’s, sorequire PDLloads nothing untilimport, anduse PDLgives upstream’s exports, now includingrcols,wcols,rfits,wfits,rpic,wpic,rimandwim, less 22 functions pperl does not have yet. PDL::IO::Misc, PDL::IO::Pnm, PDL::IO::FITS, PDL::IO::Pic, PDL::ImageRGB, PDL::Image2D and PDL::Compression load, PDL::Types and PDL::Constants are upstream’s, anduse PDL::LiteFno longer leaves every operation dying with “PDL core struct is NULL”. A pperl built with PDL loads libquadmath when PDL::Core loads, no longer at startup, where it slowed every number-to-string conversion of every program.PDL computations that gave wrong answers now match PDL 2.104:
medianandoddmedianof a multi-dimensional ndarray,avg, sums of byte and 64-bit ndarrays,stats,dim(-1),nbadandapprox; seededrandomnumbers equal perl5’s bit for bit. In-place operations on a converted type ($d &= 0; $d |= 1on doubles) no longer die and leak,catof ndarrays of different types no longer writes garbage, andclip,hclipandlcliptake->inplace, an output and undef bounds as upstream does (lclipdied).PDL errors and operators follow upstream: errors go through
PDL::barfwith its backtrace, operations upstream does not run in place refuse->inplace(a reduction had written its result over its input), andne,lt,gt,le,geandcmpon ndarrays die as on perl5 instead of comparing element by element.matmultandxreport upstream’s dimension-mismatch message and promote vectors as upstream does,-$xkeeps an unsigned type, andabsof a complex value is its modulus. Loading PDL under-wno longer warns about redefined subroutines, and the binary operators read their swap flag as an integer, running get-magic. An out-of-range dimension given tobroadcastcroaks at the first computation, where perl5 reads memory it does not own.PDL multithreading: as in PDL 2.104, an operation on an ndarray of at least 1 Mi elements is split across threads by default, with upstream’s controls (
set_autopthread_targ,set_autopthread_size,online_cpusand the rest); the thread count follows--threads, and--no-parallelturns the splitting off. Warnings and errors raised in the threads arrive after the join throughPDL::cluckandPDL::barf, as upstream’s do. Reading ASCII and PNM data is not split, where upstream’s threaded readers crash.PDL subclasses and redefinitions are honoured by the functions pperl runs natively for speed -
new,null, the type constructors,flat, whole-array reductions such assumandmax,at,setand string conversion: each takes its shortcut only for a plain PDL while the subs upstream’s Perl would call are unchanged, and runs upstream’s Perl otherwise. A list assignment toflat’s result no longer clobbers the caller’s variable, a format argument to string conversion is no longer ignored, and string conversion clears$@as upstream’s does.Imager follows Imager 1.036, including its paletted images: every palette has 256 entries, and an index past the colours in use is rejected or read as black. Imager::Font::FT2 is native, over FreeType loaded at run time, and the Imager and FT2 tests match perl5 with the original Imager 1.036.
Exporter, Carp and File::Temp are perl 5.44’s own modules (Exporter 5.79, Carp 1.54) instead of pperl’s replacements, and the modules built into pperl export through the real Exporter as their originals do:
Errno->can('import')is\&Exporter::import, and Config no longer inherits from Exporter.List::Util:
reducewith a block that branches, such asreduce { $a > $b ? $a : $b } 1 .. 500, no longer hangs with the JIT on, and it compiles: 2000 reductions over 2000 elements take 314M cycles against 363M on perl5.minandmaxover more than 65,536 numbers return the element itself, as perl5 does, not a new number:maxof numeric strings returns"0300000", not300000.EV and Event: an EV periodic watcher’s callback no longer leaks two values per call, which had kept the watcher from ever being freed, and setting its reschedule callback takes effect, where EV 4.37 never calls it or fails every fire. EV’s signal names IOT, CLD and POLL mean their signals instead of out-of-range numbers.
Event::queuewith a wrong number of arguments dies with a usage message instead of crashing, and destroying anEvent::generic::Sourcethat still has watchers no longer frees it twice.Cairo checks its methods’ arguments before reading them and reports a wrong call as the Cairo module does (
Usage: Cairo::Matrix::translate(matrix, tx, ty)), and theMIME_TYPE_*andOUTLINE_ROOTconstants are constants folded at compile time.Native modules in release builds are part of the profile-guided optimisation training: List::Util, Scalar::Util, Encode, MIME::Base64, Digest::MD5, Digest::SHA, Storable, Data::Dumper, Coro and others had been built as code the training never ran. Digest::SHA’s final padding, Encode’s single-byte encoder and Cwd’s
abs_pathalso no longer do work their C originals skip.%Config describes what pperl provides:
nonxs_ext,extensionsandknown_extensionsname its pure-Perl and native extensions as Configure would, andbin,scriptdir,sitebin,sitescript,vendorbin,vendorscriptand their*expforms are defined, as in perl 5.44. Test suites that check$Config{extensions}no longer skip, and Pod::Find no longer warns about an uninitialisedscriptdir.Peta modules are versioned 0.2.0, below 1.0.0 until their interfaces are final; Peta::XS, which was 1.0, moves down with them.
Peta::FFI::UUID’suuid_unparseanduuid_is_nulldie on anything but a 16-byte UUID instead of padding or cutting it.Smaller module changes. Sub::Exporter::Progressive 0.001013 ships, for File::Map. POSIX functions that return a system call’s status give a modifiable undef on failure,
Config::config_releaves a bad pattern’s error in$@, Digest::SHA leaves its argument downgraded as perl5 does,Coro::State::nicereturns the previous priority,Math::GMP::_gmp_build_versionexists, and Errno reports its version as 1.39. GD and Term::ReadLine::Gnu without their C library fail atrequire, and GD’s writers croak on failure,interlaced(0)turns interlacing on andalphaBlendingandsaveAlphareturn nothing, all as the GD module does.
Command line#
CPU features:
pperl -Vlists the CPU extensions the binary was compiled for and those the JIT uses on the machine it runs on;--jit-statsprints them as acpu=line.
Distribution#
Minimal builds: a pperl built without the standard native modules (
native-min) answersrequire Imager,GD::Simple,Memoize::ExpireorSDL2::Imagewith “Can’t locate …” naming the missing capability, instead of loading the Perl half and failing later.
Documentation#
Changelog: this changelog, with one page per version from 0.5.6 on. Every change of a version is accounted for, either in an entry or as internal work.
Index: one page per initial letter, plus one for symbols, under a letter overview. Every entry is the name itself and links to its page; a function also appears under its own name with its module in parentheses, as in
access (POSIX), and a pragma asopen (pragma)beside the built-inopen. Besides modules and functions it now lists perl’s special variables and operators under perl’s own index terms, the command-line options and environment variables, the topics of every guide and tutorial, and every version of this changelog, with each module under the versions that changed it.^^and^^=: the operator reference documents perl’s high-precedence logical exclusive or; it said there was no^^.Errata: the chapter on errors in perl’s own documentation is withdrawn. A page removed from the documentation is now also removed from the published site, where it had stayed reachable by its address.
Module reference: the native modules’ pages document what a user calls, with synopses whose output is the same on perl 5.44 and pperl, the upstream version implemented, and a section on differences from upstream citing the tests that pin them. Every function is published under its Perl name (
B::CV::GV, notcv_gv); PDL, Net::SSLeay, EV, Event, Storable, POSIX, Coro, Compress::Raw::Zlib, Unicode::Normalize, Peta::LMDB and the DBM modules gained full references, and Cairo’s pages moved fromPeta::FFI::CairotoCairo.Bundled modules: the 755 pure-Perl modules embedded in pperl have reference pages made from their own POD, with highlighted code and corrections where the original text does not apply to pperl; text about building XS modules is left out. XSLoader’s page describes how it starts native modules under pperl.
Graphics guides: the 2D and 3D graphics guides are translated into all 41 languages of the documentation, and factual errors in their English were corrected.
Translations: the German translation covers the native module reference pages, SDL2, Peta::WebGPU and the debugging guide, and every language’s catalogues follow the reference pages to their new places.