# pperl 0.6.16 *2026-08-29 to 2026-09-06, 134 commits.* ## JIT and parallelization - **Regular expressions** are compiled to machine code once a pattern has searched 64 KiB of text. The compiler covers literals, character and POSIX classes, alternations, captures, greedy and non-greedy quantifiers, `\b` and `\B`, and case-insensitive matching under `/aa`; a pattern it cannot compile runs on the regular engine and, once refused, costs next to nothing per match. It is on by default, and `--no-jit` turns it off together with the JIT for Perl code. Its results matched perl5's on all 29 files of PCRE2's regression test corpus. - **Regex search**: a compiled pattern finds candidate start positions 16 bytes at a time, testing up to three of its leading characters at once, also when it starts with an alternation, a character class or a case-insensitive literal. An anchored pattern is tried only where it can match. The Benchmarks Game's regex-redux at 250,000 runs in 1.74 billion instructions, against 9.80 billion in 0.6.15. - **Numeric loops** keep their variables in machine registers also when the loop body reads and writes array elements, and their intermediate results no longer pass through Perl values; each array is checked once when the loop is entered instead of at every statement. The Benchmarks Game's nbody at 200,000 steps runs 35.8% fewer instructions, and the Benchmarks Game's mandelbrot 14.1% fewer at size 400 and 7.7% fewer at size 120. - **Nested counted loops**: a `for` loop over a range nested in a compiled loop, such as `for my $j ($i + 1 .. $n)`, with a lexical loop variable or without one, no longer pushes a Perl loop context on every pass. Its counter lives in a register, and the context is built only if control leaves compiled code while the loop is open. nbody runs another 34.8% fewer instructions, 2.80 billion against 4.29 billion. - **Compound assignment** to array and hash elements compiles: `$a[$i] += $x`, `-=` and `*=` no longer keep their whole loop from compiling, and nbody, which updates its velocities this way, runs 18.25% fewer instructions. `|=`, `&=`, `^=`, `<<=` and `>>=` compile on scalar variables, and `||=`, `&&=` and `//=` on lexical and package scalars. - **Hash elements** with a constant or lexical key, `$h{$k}` and `$h{'name'}` on a lexical hash, compile in a loop both as values and as assignment targets, as in `$sum += $h{$k}` or `$h{$k} += 1`; before, only a computed key compiled. Appending one to a string, `$s .= $h{'name'}`, compiles too. - **Package variables** no longer keep a loop from compiling when they are incremented or assigned: `$count++` and `$total = $total + $x` on a package variable compile, as does assigning a package variable or a conditional expression to an array or hash element. - **Increments in expressions**: `++` and `--` on an array or hash element compile when their result is used, as in `my $old = $a[$i]++`, and so do `$x = $g++` on a package variable and `($c ? $x : $y)++`. - **Many lexical variables**: loops in a sub or file with many lexical variables compile as they do in small ones. Variables and temporaries beyond the 64th of their sub or file were never kept in registers, and some statements using them, such as `$t{"k$i"} += 1`, kept their loop from compiling. - **Short loops** that cannot repay the cost of compiling them stay interpreted: a counted `for` loop is compiled only once it has run, or its bounds show it will run, enough iterations for its size, counted across all its runs. Loops that call a sub, and `while` loops, compile as before. Short programs whose loops ran a few hundred times used 4 to 8 times the instructions they need with `--no-jit`. - **Subroutine calls with computed arguments** in a compiled loop, such as `f($i, $i + 1, $i + 2)`, are compiled into the loop again; in 0.6.15 such a loop ran no faster than without the JIT. A loop making a three-argument call runs a 15.6th of the instructions, and every Benchmarks Game program measured ran fewer as well. - **Numeric subroutine calls** from a compiled loop pass their arguments in registers when the sub takes two numbers through `my ($a, $b) = @_` or reads `$_[0]` and `$_[1]` directly. A loop calling a sub that reads `$_[0]` directly could crash or die with "Modification of a read-only value"; it now gives perl5's results. - **Conditional assignments** such as `my $v = $c ? 1 : $i / 7;` followed by arithmetic on `$v` give perl5's result in a compiled loop. If only the integer branch had run when the loop was compiled, the fraction was later read as an integer, and the sum was wrong but plausible. - **Element stores** in a compiled loop keep a value's string: with `$x` holding `"0.0"` after a numeric read, `$a[$i] = $x` stored `0` and now stores `"0.0"`, as perl5 does. The same applied to hash elements and to slice assignments. - **Crashes in compiled loops**: some nested loops, among them an inner `for (1 .. $n)` whose body never reads `$_`, ended in a segmentation fault in compiled code. They now run as on perl5. ## Modules - **SDL2** is a new native binding to libSDL2: windows, renderers, textures, OpenGL contexts, events and timers, both as SDL's own C functions one for one and as objects (`SDL2::Window`, `SDL2::Renderer`, `SDL2::Texture`, `SDL2::GLContext`) that free their handles when they go out of scope. `SDL2::Surface`, `SDL2::Image` and `$renderer->save_png`, `save_jpg` and `save_bmp` load and save images, and `$renderer->set_target($texture)` draws into a texture. Its 709 constants are taken from the installed SDL2 headers, not typed in. - **GD** loads: `use GD` reaches the system's libgd through a native binding, and GD's own Perl modules, including `GD::Polygon`, `GD::Polyline` and `GD::Simple`, run unchanged on top of it. Images can be drawn, compared and written as PNG and JPEG. `$im->clone` after `filledPolygon` returns a correct copy, where perl5's GD crashes on a libgd defect. - **Imager** loads, as a first part: images of all four kinds can be created, described, and have their pixels written and read back, with `Imager.pm` and its Perl modules running unchanged. Drawing, file formats, transforms, filters and fonts are not yet available. - **Peta::PCRE2** is a new native module that binds the system's libpcre2-8: `new`, `match`, `count`, `substitute`, `capture_count`, `jit`, `version` and `jit_available`, and 12 `PCRE2_*` constants. Without libpcre2 installed, its functions croak. - **Cairo** without libcairo installed croaks with the name of the package to install on every call; before, the second call hung. ## Command line - **`--no-regex-jit`** turns off the regex compiler and leaves the JIT for Perl code running; `PPERL_RXJIT=0` does the same from the environment, and `--help` describes both. `PPERL_RXJIT_STATS=1` reports for each pattern whether it was compiled or refused, and why. ## Distribution - **Profile-guided builds** of the release binary now train on programs that exercise the JIT and the regex compiler; before, those parts were barely run during training and were optimised for size. On the first build with the extended training, every Benchmarks Game program measured ran fewer instructions, the Benchmarks Game's revcomp 6.2% fewer. ## Documentation - **3D graphics guide**: a new guide with chapters on the mathematical foundations, a software rasteriser, shading, meshes, model formats (OBJ and STL readers and writers), modelling operations, scene graphs, and opening a window with an OpenGL context through SDL2. Every example was run, and the outputs shown are what pperl printed. - **2D graphics guide**: the GUI chapter and both capstone projects, a paint program and a game, are rewritten on the native SDL2 binding and run on pperl. The chapters on `Peta::FFI` now cover callbacks and its memory primitives, which they had called out of reach. - **Module reference**: 49 new pages, among them `Peta::PCRE2`, 18 for Cairo and 14 for PDL, and the catalogues of all 41 translation languages were updated to match.